Metadata-Version: 2.4
Name: keble-keepa
Version: 1.4.3
Author-email: zhenhao-ma <bob0103779@gmail.com>
Requires-Python: <3.14,>=3.13
Requires-Dist: aiohttp<4.0.0,>=3
Requires-Dist: keble-db<2.0.0,>=1.9.0
Requires-Dist: keble-exceptions<1.0.0,>=0
Requires-Dist: keble-helpers<2.0.0,>=1.54.0
Requires-Dist: python-dateutil<3.0.0,>=2
Requires-Dist: requests<3.0.0,>=2
Provides-Extra: test
Requires-Dist: pytest-asyncio<1.0.0,>=0; extra == 'test'
Requires-Dist: pytest-recording<1.0.0,>=0; extra == 'test'
Requires-Dist: pytest<9.0.0,>=8; extra == 'test'
Requires-Dist: vcrpy<8.0.0,>=6; extra == 'test'
Description-Content-Type: text/markdown

# keble-keepa

The source line requires `keble-helpers>=1.54.0` so exact Keepa token/call
usage uses the same public contract in development and registry installs.
Provider pricing remains outside this package.

Side effects if changes:

- Amazon provider services and Data Infra transport these usage events.
- Changing the helper floor requires lock and downstream raw API verification.

`keble-keepa` is Keble's Keepa API and cache package. It owns Keepa object
schemas, sync/async API access, Mongo cache CRUD, batch loading, time
conversion helpers, and pytest fixtures/fakes for downstream packages.

## Verified State

Checked on 2026-07-18 against `pyproject.toml`, package exports,
`keble_keepa/`, and tests.

- Package: `keble-keepa 1.4.3`
- Python: `>=3.13,<3.14`
- Critical deps: `aiohttp`, `requests`, `keble-db`, `keble-helpers`,
  `keble-exceptions`, `python-dateutil`
- Source roots: `api/`, `crud/`, `schemas/`, `testing/`, `utils/`
- Public exports: Keepa API objects, batch loader, cache helpers, time
  conversion helpers, and the `keble_keepa.testing` pytest plugin.

Install the current public release with
`pip install "keble-keepa>=1.4.3,<2"`. Version 1.4.3 requires the shared
`keble-db>=1.9.0` persistence contract and the shared upstream-failure plus
exact-money vocabulary in `keble-helpers>=1.54.0`.

## Keepa Price Integer Boundary

Keepa price histories are normalized internally to fixed hundredths of a major
currency unit, including synthetic `* 100` normalization for JPY. `Product`
converts that provider-specific integer to exact Decimal major units before
constructing canonical `Money`; the shared ISO exponent then stores USD cents,
JPY yen, and other currencies correctly. Never pass a Keepa price integer
directly to `Money.amount`.

## Runtime Contract

- Sync Keepa calls use `requests`; async Keepa calls use `aiohttp` and should
  respect standard proxy environment variables when configured by callers.
- Bestseller boards expose both `request_best_sellers` and genuine-aiohttp
  `arequest_best_sellers`; the async method uses the same typed Redis/Mongo
  cache key and emits usage only after real upstream I/O.
- Every typed response retains Keepa's `tokensConsumed` field. Async cache
  misses emit that exact provider token count through
  `UsageAccountingRecorderProtocol`; missing or zero reports remain
  `USAGE_ONLY` and therefore billing-unknown rather than being inferred free.
- Cache hits emit no usage event even though cached response metadata is
  retained for traceability.
- Short-lived Redis response serialization uses Pydantic JSON mode so exact
  Decimal refill/capacity values survive successful live calls and cache replay.
- Every physical Keepa request is attempted once. The package does not sleep,
  retry, recursively split failed batches, or silently drop failed ASINs;
  provider exceptions reach Data Infra intact and durable retry belongs to the
  Platform worker.
- Rejections retain HTTP status, request ID, typed Keepa error data, and exact
  capacity evidence. `refillIn` is milliseconds, negative `tokensLeft` remains
  valid evidence, and gross refill is distinct from token-flow reduction.
- Failed async HTTP responses emit provider-reported `tokensConsumed` usage
  before the typed exception is raised; non-JSON failures remain usage-unknown
  rather than being inferred free.
- Cache reads and writes must stay explicit about key, marketplace, and
  freshness behavior. Do not hide broad cache scans behind convenience APIs.
- Downstream tests should use `keble_keepa.testing` fixtures/fakes before
  inventing package-local Keepa mocks.

## Testing

```bash
uv sync --group test
RUN_INTEGRATION=1 RUN_REAL_DB=1 RUN_LOCAL_STACK=1 RUN_DB_STACK=1 \
  uv run pytest -q -m "not live and not container"
uv run pytest -q -m "not live and not slow and not eval and not local_stack and not db_stack and not container"
uv run pytest -q -m eval
npx --yes pyright .
```

The first command is the local-full Keble development proof and runs
Mongo-backed integration tests with isolated cache collections. The second is
the portable-offline fallback for no-DB machines. Selecting `-m eval` is the
eval opt-in; there is no generic `RUN_EVALS` gate.

Real Keepa calls belong only in marked/env-gated live tests.
Package-owned test helpers live under `keble_keepa.testing`; do not recreate
root-level `tests/config.py`, root test packages, or legacy `tests/test_*`
source folders.

Mongo/Redis/Keepa live settings load explicit process env first, then
`tests/.env`, then the umbrella `keble.backend/.env` through
`keble_db.testing.resolve_test_env`. Set
`KEBLE_BACKEND_ENV_FILE=/path/to/.env` from unusual worktree or CI layouts.
Do not copy backend secrets into this repo.

Live Keepa canaries should make one real provider call against an isolated cache
collection. Do not add Keepa response-cache replay to live tests for speed;
cache correctness belongs in integration tests with fake/shared fixtures.

Async cache tests receive a process-unique namespaced `ExtendedAsyncRedis` and
delete all owned keys after every test. Keep this isolation when adding new
cache consumers; an un-namespaced wrapper is a test and production leak risk.

## Change Logs

Historical release notes and long AI change history live under `logs/`.

## Product release metadata

`Product.release_date_observation` parses Keepa `YYYY`, `YYYYMM`, and
`YYYYMMDD` values into a typed interval with retained precision. Consumers must
use the interval for recency decisions; they must not invent a month or day for
partial dates or substitute offer-history timestamps. `original_language_code`
normalizes Keepa's finite language names to ISO codes and returns `None` when
the source does not declare a recognized language.

## Side Effect If Changes

- Backend report generation, product discovery, `keble-keepa-analysis`, and
  AMZ product-report workflows depend on this package for Keepa access.
- Cache query/index changes can affect production cache repair and live
  report correctness; verify query shape and cleanup.
