Skip to main content

A fully typed, async Python wrapper for the Last.fm API

Project description

lasty

A fully typed, modern, and asynchronous Python client for the Last.fm API.

Python Version Asyncio Type Checked License


lasty is an asynchronous Python library for interacting with the Last.fm API. Built on top of aiohttp, it provides clean abstractions, robust error handling, auto-pagination, and complete static typing (mypy --strict).

Features

  • Asynchronous: Built natively on aiohttp for non-blocking HTTP requests.
  • Strictly Typed: Every endpoint, payload, and response is backed by typed frozen dataclasses.
  • API Mirroring: Namespaces match the official Last.fm API hierarchy (e.g. client.user.get_info()).
  • Error Handling: Dedicated exception hierarchy mapped directly to Last.fm error codes.
  • Automatic Retries: Exponential backoff for rate limits (HTTP 429) and temporary server errors (HTTP 5xx).
  • Auto-Pagination: iter_* helpers to asynchronously stream through paginated endpoints.
  • Request Signing: Automatic MD5 signature generation for write operations like scrobbling.

Installation

pip install lasty

Requires Python 3.10 or higher.

Quick Start

import asyncio
from lasty import LastFM, Period

async def main():
    async with LastFM(api_key="your_api_key") as client:
        # Fetch user information
        user = await client.user.get_info("Smugaski")
        print(f"User: {user.name} | Total Scrobbles: {user.playcount}")

        # Fetch recent tracks
        recent = await client.user.get_recent_tracks("Smugaski", limit=5)
        for track in recent.items:
            if track.now_playing:
                print(f"Now playing: {track.artist_name} - {track.name}")
            else:
                date_str = track.date.text if track.date else 'unknown'
                print(f"Played: {track.artist_name} - {track.name} at {date_str}")

if __name__ == "__main__":
    asyncio.run(main())

Core Concepts

API Namespaces

The lasty client exposes namespaced sub-clients matching the official Last.fm API documentation:

  • client.user (e.g. .get_info(), .get_recent_tracks(), .get_top_artists())
  • client.artist (e.g. .get_info(), .get_similar(), .search())
  • client.album (e.g. .get_info(), .search(), .add_tags())
  • client.track (e.g. .get_info(), .scrobble(), .love())
  • client.tag (e.g. .get_info(), .get_top_tracks())
  • client.chart (e.g. .get_top_artists(), .get_top_tracks())
  • client.geo (e.g. .get_top_artists("Poland"))
  • client.library (e.g. .get_artists())
  • client.auth (e.g. .get_token(), .get_session())

Pagination & Iterators

For endpoints returning paginated lists, lasty offers iter_* companion methods returning AsyncIterator instances:

import asyncio
from lasty import LastFM, Period

async def get_top_artists():
    async with LastFM(api_key="your_api_key") as client:
        async for artist in client.user.iter_top_artists("Smugaski", period=Period.SEVEN_DAY, limit=50, max_items=200):
            print(f"Artist: {artist.name} (Playcount: {artist.playcount})")

Authenticated Write Operations

Write operations (such as scrobbling or loving a track) require api_secret and a user session_key. lasty handles request signing automatically.

import asyncio
import time
from lasty import LastFM

async def auth_examples():
    async with LastFM(
        api_key="your_api_key", 
        api_secret="your_api_secret", 
        session_key="user_session_key"
    ) as client:
        
        # Love a track
        await client.track.love("Rammstein", "Sonne")
        
        # Scrobble a track
        result = await client.track.scrobble(
            artist="Rammstein",
            track="Sonne",
            timestamp=int(time.time()),
        )
        print(f"Scrobble result: {result.accepted} accepted, {result.ignored} ignored.")

Authentication Flow

To acquire a user session_key:

async with LastFM(api_key="key", api_secret="secret") as client:
    token = await client.auth.get_token()
    auth_url = client.auth.get_auth_url(token)
    print(f"Authorize the application at: {auth_url}")
    input("Press Enter after authorizing...")
    
    session = await client.auth.get_session(token)
    print(f"Session Key: {session.key}")
    print(f"User: {session.name}")

Error Handling

API errors raise typed exceptions inheriting from LastFMError:

from lasty import LastFM
from lasty.errors import InvalidParametersError, RateLimitError

async def handle_errors():
    async with LastFM(api_key="your_api_key") as client:
        try:
            await client.user.get_info("nonexistent_user_123456789")
        except InvalidParametersError as e:
            print(f"User not found: {e}")
        except RateLimitError:
            print("Rate limit exceeded.")

Development

Install development dependencies and the package in editable mode:

pip install -e .[dev]

Run tests and type checks:

python -m pytest tests/
mypy lasty --strict

License

This project is licensed under the Apache 2.0 License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lasty-0.1.0.tar.gz (41.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lasty-0.1.0-py3-none-any.whl (44.6 kB view details)

Uploaded Python 3

File details

Details for the file lasty-0.1.0.tar.gz.

File metadata

  • Download URL: lasty-0.1.0.tar.gz
  • Upload date:
  • Size: 41.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for lasty-0.1.0.tar.gz
Algorithm Hash digest
SHA256 99665091a7fcfc5269073fa969f3eadae72717690bc8a12fc299966f3f53a9bc
MD5 ac9464248fec10a5e8d5526e507ce49c
BLAKE2b-256 d24a1276f4e479e711d6fec9f1b0c941230cd83ca1ce8151d8a819ee6cd6adcd

See more details on using hashes here.

File details

Details for the file lasty-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: lasty-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 44.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for lasty-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c5a7f19a84a109404076ebb83c5df3a0f076ca83d125468dd1c99275559617e0
MD5 57183093d87e77c7fe766b81f6021742
BLAKE2b-256 3a2ebbabe4e521b287e9d6f055d2633897231451c5ccd09a99d0b09c4cba6119

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page