Skip to content

Quickstart⚓︎

This tutorial will walk you through the basics of using PokeLance, from setting up a client understanding it's lifecycle to fetching resources from the PokéAPI.

Creating a client⚓︎

The simplest possible client needs no arguments at all:

Python
import asyncio
from pokelance import PokeLance

client = PokeLance()


async def main() -> None:
    print(await client.ping())
    await client.close()  # always call close() when you're done with a client to free up resources


asyncio.run(main())
Text Only
0.03394241000000875

PokeLance() lazily creates its own aiohttp.ClientSession on the first request. That's convenient for quick scripts, but for anything long-lived (bots, web servers) prefer the async context manager below so the session is guaranteed to close.

With an async context manager⚓︎

Python
import asyncio
import aiohttp
from pokelance import PokeLance


async def main() -> None:
    async with aiohttp.ClientSession() as session, PokeLance(session=session) as client:
        print(await client.ping())
        berry = await client.berry.fetch_berry("cheri")
        print(berry.name)
    # session and client are both closed here automatically


asyncio.run(main())
Python
import asyncio
from pokelance import PokeLance


async def main() -> None:
    async with PokeLance() as client:
        print(await client.ping())
        berry = await client.berry.fetch_berry("cheri")
        print(berry.name)


asyncio.run(main())

More on Async Context Managers

To learn more about async context managers and why they are useful, refer to this article: Async Context Managers in Python.

Bring your own session

Passing your own aiohttp.ClientSession is useful when PokeLance is one HTTP client among several in your app (e.g. another API client, or an internal service). It allows you to share the same session and connection pool across multiple clients, which can improve performance and resource usage.

Fetching a few resources⚓︎

This example hits the https://pokeapi.co/api/v2/ endpoint to fetch a berry, its flavor, and its firmness:

Python
import asyncio
from pokelance import PokeLance

client = PokeLance()


async def main() -> None:
    latency = await client.ping()
    berry = await client.berry.fetch_berry("cheri")
    flavor = await client.berry.fetch_berry_flavor(berry.flavors[0].flavor.name)
    firmness = await client.berry.fetch_berry_firmness(berry.firmness.name)
    print(f"ping: {latency:.4f}s")
    print(f"berry: {berry.name} (id={berry.id}, growth_time={berry.growth_time})")
    print(f"flavor: {flavor.name}")
    print(f"firmness: {firmness.name}")
    await client.close()


asyncio.run(main())
Text Only
ping: 0.0389s
berry: cheri (id=1, growth_time=3)
flavor: spicy
firmness: soft

Fully Typed Models

All models and each of their fields are fully typed with attrs so you can use your favorite IDE's autocomplete and type checking features to explore the API surface.

Rendering a sprite inline⚓︎

The client includes a convenience method for fetching related media resources (images and audio) present in PokéAPI with some additional guardrails to ensure the response is valid and also includes a simple caching layer to avoid repeated network requests for the same resource. This example fetches Pikachu's front sprite and renders it inline as a base64-encoded <img> tag:

Python
import asyncio
import base64
from pokelance import PokeLance

client = PokeLance()


async def main() -> str:
    pokemon = await client.pokemon.fetch_pokemon("pikachu")
    sprite = await client.get_image_async(pokemon.sprites.front_default)
    await client.close()
    encoded = base64.b64encode(sprite).decode("ascii")
    return (
        f'<img src="data:image/png;base64,{encoded}" '
        f'alt="{pokemon.name} sprite" width="96" height="96"/>'
    )


print(asyncio.run(main()))
pikachu sprite

See Media for the full breakdown of get_image_async/get_audio_async.

Reading the response as a dict⚓︎

Every model inherits to_dict() (see BaseModel), which recursively serializes attrs models and enums back into plain Python data, handy for logging, inspecting the raw payload, or just dumping it to JSON.

Python
import asyncio
import json
from pokelance import PokeLance

client = PokeLance()


async def main() -> str:
    berry = await client.berry.fetch_berry("cheri")
    await client.close()
    return json.dumps(berry.to_dict(), indent=2, default=str)[:600] + "\n..."


print(asyncio.run(main()))
JSON
{
  "id": 1,
  "name": "cheri",
  "growth_time": 3,
  "max_harvest": 5,
  "natural_gift_power": 60,
  "size": 20,
  "smoothness": 25,
  "soil_dryness": 15,
  "firmness": {
    "name": "soft",
    "url": "https://pokeapi.co/api/v2/berry-firmness/2/"
  },
  "flavors": [
    {
      "potency": 10,
      "flavor": {
        "name": "spicy",
        "url": "https://pokeapi.co/api/v2/berry-flavor/1/"
      }
    },
    {
      "potency": 0,
      "flavor": {
        "name": "dry",
        "url": "https://pokeapi.co/api/v2/berry-flavor/2/"
      }
    },
    {
      "potency": 0,
      "flavor": {

...

berry.raw is also always available if you need the exact untouched JSON PokéAPI sent back, before any model parsing happened.

Cache-then-fetch, by hand⚓︎

Every resource has a synchronous get_* (cache-only) and an asynchronous fetch_* (cache-or-network) counterpart. This is the idiom used throughout the whole library:

Python
print(client.berry.get_berry("cheri"))          # None on a cold cache
print(await client.berry.fetch_berry("cheri"))  # hits network, populates cache
print(client.berry.get_berry("cheri"))          # now cached, instant

See Fetching Data for the full rationale, and getch_data for a single call that does both steps for you across any extension.

Next steps⚓︎

Comments