Metadata-Version: 2.5
Name: scwiki
Version: 0.1.0
Summary: Python client for the Star Citizen Wiki API with version-aware caching and pluggable HTTP transports
Project-URL: Homepage, https://github.com/Garulf/scwiki
Project-URL: Issues, https://github.com/Garulf/scwiki/issues
Author: Garulf
License-Expression: MIT
License-File: LICENSE
Keywords: api,cache,client,star-citizen,wiki
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Games/Entertainment
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.11
Provides-Extra: aiohttp
Requires-Dist: aiohttp>=3.9; extra == 'aiohttp'
Provides-Extra: httpx
Requires-Dist: httpx>=0.27; extra == 'httpx'
Description-Content-Type: text/markdown

<!-- generated by readwright from README.md.j2; edit the template, not this file -->
# scwiki

Python client for the Star Citizen Wiki API with version-aware caching and pluggable HTTP transports

[![PyPI](https://img.shields.io/pypi/v/scwiki)](https://pypi.org/project/scwiki/) [![Python](https://img.shields.io/pypi/pyversions/scwiki)](https://pypi.org/project/scwiki/) [![CI](https://img.shields.io/github/actions/workflow/status/Garulf/scwiki/ci.yml)](https://github.com/Garulf/scwiki/actions/workflows/ci.yml) [![License](https://img.shields.io/github/license/Garulf/scwiki)](https://github.com/Garulf/scwiki/blob/main/LICENSE) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

## Installation

```sh
uv add scwiki          # or: pip install scwiki
```

The base package has no third-party dependencies. The sync client uses the standard library, and the async client needs one of:

```sh
uv add "scwiki[httpx]"
uv add "scwiki[aiohttp]"
```

Requires Python 3.11 or newer.

## Usage

```python
import asyncio
from scwiki import AsyncClient

async def main():
    async with AsyncClient() as sc:
        ship = await sc.vehicles.get("aegs-avenger-stalker", include=["ports"])
        print(ship.name, ship.speed.scm, ship.meta.version)

        async for v in sc.vehicles.list(filter={"manufacturer": "Aegis Dynamics"}, limit=20):
            print(v.slug)

        groups = await sc.search("carrack")
        carrack = await groups[0].results[0].fetch()

asyncio.run(main())
```

The sync client has the same surface:

```python
from scwiki import Client

with Client() as sc:
    build = sc.game_versions.default()
    weapon = sc.weapons.get("a03-canuto-sniper-rifle", locale="de_DE")
    for system in sc.starsystems.list():
        print(system.code, system.name)
```

Every resource family the API exposes is a namespace on the client: `vehicles`, `ground_vehicles`, `gravlev_vehicles`, `items`, `weapons`, `armor`, `clothes`, `food`, `vehicle_items`, `vehicle_weapons`, `weapon_attachments`, `commodities`, `missions`, `locations`, `blueprints`, `celestial_objects`, `starsystems`, `comm_links`, `comm_link_images`, `galactapedia`, `manufacturers`, `factions`, `game_versions`, `shipmatrix_vehicles`, `stats`. Each has `get()` and `list()`, and families with a filters endpoint have `filters()`.

Models are frozen dataclasses with the fields most callers need typed. The untouched API payload is always available as `.raw`, and `.meta` tells you the game build, whether the record came from cache, and whether it is stale.

## Caching

The API has no ETags, so the client decides freshness itself. It knows two kinds of data:

- **Game data** (vehicles, items, weapons, missions, locations, and so on) is scoped to a game build and never changes for a given build. Records are cached forever under their build. The client resolves the current build with one cheap request that is itself cached for an hour, so a new build is picked up lazily, one record at a time, with no purge storm. Pin a build with `Client(version="4.9.0-LIVE.12232306")` or per call and the probe is skipped.
- **Everything else** (comm-links, galactapedia, starmap, manufacturers, ship matrix, stats) expires on a per-family TTL: a week for comm-link and galactapedia articles, a day for starmap and manufacturer data, an hour for lists and stats.

The default store is SQLite in `$XDG_CACHE_HOME/scwiki/` (or `%LOCALAPPDATA%\scwiki\` on Windows) in WAL mode, so a bot and a plugin can share one file. An in-process LRU sits in front of it. Search results are only kept in the LRU for five minutes because the search endpoint is rate-limited to 60 requests per minute; the client throttles itself to stay under that.

If the API is down or returns a 5xx after retries, an expired entry is served with `meta.stale = True`. Once the cache is warm the client works offline.

```python
from scwiki import Client, MemoryStore, SqliteStore, LruFront

sc = Client()                                   # SQLite + LRU, the default
sc = Client(cache=MemoryStore())                # per-process only
sc = Client(cache=LruFront(SqliteStore("/srv/scwiki.db")))
sc = Client(cache=None)                         # no caching at all

sc.vehicles.get("aegs-avenger-stalker", fresh=True)   # bypass the cache for one call
sc.purge_cache(version="4.9.0-LIVE.12232306")         # drop an old build
sc.purge_cache(family="comm_links")
```

## Bring your own HTTP library

The client never imports an HTTP library. It talks to a transport with one method, `send(Request) -> Response`, and ships adapters for urllib (default for the sync client), httpx and aiohttp. Pass your own if you already have a configured session:

```python
import aiohttp
from scwiki import AsyncClient
from scwiki.transports.aiohttp import AiohttpTransport

session = aiohttp.ClientSession()
sc = AsyncClient(transport=AiohttpTransport(session))
```

Any object with the same two methods works:

```python
from scwiki import Request, Response

class MyTransport:
    def send(self, request: Request) -> Response:
        ...
    def close(self) -> None:
        ...
```

## Development

```sh
uv sync --all-extras --dev
uv run pytest                          # offline, fixture-driven
SCWIKI_NETWORK=1 uv run pytest tests/contract   # live transport checks
uv run tox                             # full matrix, lint, types
uv run python scripts/refresh_fixtures.py       # re-record fixtures from the live API
```

## Attribution

Data comes from the community-maintained [Star Citizen Wiki API](https://api.star-citizen.wiki) ([source](https://github.com/StarCitizenWiki/API)). This is a fan project and is not affiliated with or endorsed by Cloud Imperium Games.

## Contributing

Issues and pull requests are welcome at [Garulf/scwiki](https://github.com/Garulf/scwiki).

## License

MIT
