adapterkit
Тонкий коннектор сетевых адаптеров поверх librarykit. adapterkit отвечает на один вопрос: «как подключить сетевой адаптер к приложению». Он даёт декларативный контракт плагина, реестр с автодискавери через entry-points и базовый фасад адаптера — а весь сетевой движок (transport/auth/retry/errmap/ pagination/sessions/antibot/browser) реэкспортирует из librarykit, не дублируя его. Пишется один раз, переиспользуется любым доменным пакетом.
librarykit ← КОРЕНЬ: весь сетевой движок (transport/auth/retry/errmap/
pagination/sessions/antibot/browser)
▲
adapterkit ← ЭТОТ КИТ: контракт NetworkAdapter + registry (entry-points) +
BaseAdapter + orchestration_api.
Остальное — тонкий реэкспорт-шим из librarykit.
▲
домен ← конкретные адаптеры (endpoint-таблица + мапперы на сеть)
Зависимости направлены только внутрь: adapterkit зависит только от
librarykit, но НЕ от домена и НЕ от clikit (онион-граф
librarykit <- adapterkit <- clikit: clikit — слой ВЫШЕ, adapterkit его не
импортит). Адаптеры кодируются против
стабильных typing.Protocol из adapterkit.contract (структурный контракт, а не
наследование от домена). Композиция конкретных реализаций — единственный
composition root на приложение.
Установка
uv add s-adapterkit
Имя дистрибутива — s-adapterkit, имя для импорта — adapterkit. librarykit
подтянется автоматически как транзитивная зависимость.
Опциональные extra:
uv add "s-adapterkit[browser]" # Playwright — browser-login
uv add "s-adapterkit[antibot]" # curl-cffi — JA3-impersonate
uv add "s-adapterkit[oauth]" # authlib — OAuth2-flows
Быстрый старт
Описать адаптер декларативно и зарегистрировать его в реестре:
from adapterkit import BaseAdapter, Endpoint, register_adapter
class TwitterAdapter(BaseAdapter):
api_version = 1
endpoints = {
"search": Endpoint(name="search", method="GET", path="/2/tweets/search/recent"),
}
register_adapter("twitter", TwitterAdapter)
Либо отдать адаптер на автодискавери — объявить entry-point в своём pyproject.toml,
и любой потребитель adapterkit подхватит его без явного импорта:
[project.entry-points."adapterkit.adapters"]
twitter = "my_package.adapter:TwitterAdapter"
from adapterkit import discover_adapters, get_adapter_class
discover_adapters() # загрузить все плагины из entry-points
cls = get_adapter_class("twitter") # получить класс по имени сервиса
Карта модулей
| Модуль | Назначение | Реализация |
|---|---|---|
contract.py |
граничные Protocol (NetworkAdapter/Transport/Auth/ErrorMapper/Paginator/SessionStoreProtocol) + DTO (Endpoint/RequestSpec/SessionRef/Creds) + ADAPTER_API_VERSION/MIN_SUPPORTED_API_VERSION |
контракт коннектора |
registry.py |
AdapterRegistry + автодискавери через entry-points adapterkit.adapters, ленивая загрузка, ручная регистрация |
код коннектора |
base.py |
BaseAdapter (описание запроса build_request + исполнение execute) + ресурс-под-сервисы (ContentResource/CommentsResource/MetricsResource/SearchResource) — Stripe-стиль фасад |
код коннектора |
throttle.py |
ядро само троттлит плагин по его метаданным: @ratelimit, ServiceThrottle, ManagedExecutor, шов ExecutionContext |
код коннектора |
orchestration_api.py |
тонкий registry-driven API: onboard_all / health_check_all (без импортов домена) |
код коннектора |
onboarding_contract.py |
онбординг/health-контракты (LoginMode/OnboardingProtocol/HealthProtocol, api_version 2) |
реэкспорт librarykit.protocols |
errors.py |
единая иерархия ошибок | реэкспорт librarykit.errors |
retry.py |
header-driven RetryPolicy |
реэкспорт librarykit.retry |
transport.py / client.py |
HttpxTransport + choke-point HttpClient |
реэкспорт librarykit.transport |
auth.py |
TokenAuth/OAuth2Auth/CookieSessionAuth/BrowserLoginAuth |
реэкспорт librarykit.auth |
errmap.py |
декларативная карта ответ → доменная ошибка | реэкспорт librarykit.errmap |
pagination.py |
CursorPaginator (offset/cursor/page) |
реэкспорт librarykit.pagination |
sessions.py |
envelope-шифрованный SessionStore |
реэкспорт librarykit.sessions |
antibot.py |
выбор транспорта Tier 0-4 (curl-cffi JA3 / CDP) | ленивый реэкспорт librarykit.antibot |
browser.py |
warm/cold-login (требует extra browser) |
ленивый реэкспорт librarykit.browser |
Всё, что помечено «реэкспорт», — тонкий shim: единая реализация живёт в librarykit,
adapterkit лишь предоставляет её под привычным именем. Собственный код коннектора —
только contract/registry/base/throttle/orchestration_api.
Ядро само держит лимиты плагина
Плагин объявляет лимиты ОДНОЙ строкой метаданных и не пишет кода лимитов, ретраев и удержания сессии — очередь запросов и exponential backoff делает ядро:
from adapterkit import BaseAdapter, Endpoint, ratelimit
@ratelimit(calls=2, period=1) # ← всё, что плагин пишет про лимиты
class ExampleAdapter(BaseAdapter):
service = "example"
endpoints = {"get_item": Endpoint("get_item", "GET", "/items/{item_id}",
required_params=("item_id",))}
Полная инструкция (формы декларации, оси квот QuotaScope, инварианты, чек-лист для
кодогенератора) — docs/PLUGIN_LIMITS.md. Адаптер БЕЗ
объявленных лимитов работает ровно как раньше: исполнитель ядра не собирается, запросы
уходят в клиент напрямую.
Плагины-способности: единая группа skillery.plugins
Кроме ПОЛНОГО адаптера сети кит несёт вторую, независимую ось подключения —
capability: одна способность, один-два метода, ресурсы приходят аргументом
ctx: ExecutionContext (плагин не читает окружение).
from adapterkit import PluginRegistry, PublisherProtocol
registry = PluginRegistry()
registry.capabilities() # имена БЕЗ импорта чужих пакетов
registry.plugins_supporting(PublisherProtocol) # {capability_id: КЛАСС}, без инстанцирования
Формы (PluginProtocol / PublisherProtocol / TranscriberProtocol / IngestProtocol),
версия-гейт PLUGIN_API_VERSION, мост AdapterCapability поверх существующего адаптера
и чек-лист публикации — docs/PLUGIN_CONTRACT.md. Прежние
группы (adapterkit.adapters, transcribe.channels) не переименованы и работают как
раньше — новая ось добавлена рядом, а не вместо.
Ядро не тянет EXTENSIONS (ленивые слои)
Антибот и браузер — тяжёлые опциональные слои (extras antibot/browser), поэтому
import adapterkit их не загружает: реэкспорт идёт через module-level
__getattr__ (PEP 562) и срабатывает на первом обращении к имени. Практически:
import adapterkit # librarykit.antibot / .browser НЕ загружены
adapterkit.HttpClient # ядро — как раньше
adapterkit.CurlCffiTransport # ← вот здесь подгрузится librarykit.antibot
from adapterkit.browser import warm_or_autologin # ← и здесь librarykit.browser
Публичный API не изменился: from adapterkit import CurlCffiTransport,
from adapterkit.antibot import ..., from adapterkit.browser import ...,
from adapterkit import * и dir(adapterkit) работают идентично. Инвариант
закреплён fitness-тестами в tests/test_lazy_extensions.py (замер sys.modules
в отдельном интерпретаторе).
Архитектурные гейты
Канон держится инструментом, а не договорённостью. Онион-граф, ленивая граница ЯДРО/РАСШИРЕНИЯ и отсутствие скрытых зависимостей проверяются одной командой:
uv run --extra dev lint-imports
Базовый слой — зрелый import-linter;
два измерения, которых у него нет, добавлены его же механизмом плагинов
(adapterkit.gates.contracts):
eager_forbidden— ядро не поднимаетlibrarykit.browser/librarykit.antibotв момент импорта. Ленивый доступ (импорт внутри функции, PEP 562__getattr__,if TYPE_CHECKING:) нарушением не считается — встроенныйforbiddenэтого не различает;declared_dependencies— всё импортируемое объявлено вpyproject.toml(dependenciesлибо любой extra).
Рядом — conformance-наборы, которые чужой репозиторий подключает тремя строками:
AdapterContractTests (контракт NetworkAdapter) и PluginConformanceTests
(контракт способности: capability_id = имя entry-point, ctx первым
аргументом, лимиты метаданными, никакой инфраструктуры внутри плагина).
Полное описание, инструкция «как подключить у себя» и таблица «сработало —
что делать» — docs/ARCHITECTURE_GATES.md.
Новый навык за одну команду
python -m adapterkit new-skill myskill --capability publish
Порождает пакет, который уже соблюдает канон: способность с ctx-аргументом,
лимиты через @ratelimit, entry-point в группе skillery.plugins под тем же
именем, что capability_id, все четыре гейта в pyproject.toml и готовый
conformance-тест. Сгенерированный навык проходит pytest, ruff и
lint-imports без единой правки — это закреплено тестами скаффолда.
Разработка
uv sync --extra dev
uv run --extra dev pytest -q
uv run --extra dev ruff check adapterkit
uv run --extra dev lint-imports
librarykit тянется из публичной группы
gitlab.com/S-kits. Для локальной
правки кита временно укажите path-источник в [tool.uv.sources] (см. комментарий
в pyproject.toml) и выполните uv lock --upgrade.
Лицензия
MIT © 2026 Dmitry.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file s_adapterkit-0.1.6.tar.gz.
File metadata
- Download URL: s_adapterkit-0.1.6.tar.gz
- Upload date:
- Size: 259.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b89968ac0fefb0c0ed3533b127a9296afc585280e3000be4a7346631d02d4c4c
|
|
| MD5 |
a9bf9e835b0b49b8858be21a30c62440
|
|
| BLAKE2b-256 |
964ab3b5405b8adce6c8dfa4d12ff542e3001929fd364bfccda8dd3835f4cd2a
|
File details
Details for the file s_adapterkit-0.1.6-py3-none-any.whl.
File metadata
- Download URL: s_adapterkit-0.1.6-py3-none-any.whl
- Upload date:
- Size: 166.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
521ba49bae829b412883677c17970e7561702affc52fcbedc7bfad5eee8423b7
|
|
| MD5 |
e52ff645282d5490238787c93507857e
|
|
| BLAKE2b-256 |
9638fa166bcbff811332aba7e2b44098fd876b1af6f59feb0283f562957ea1e3
|