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.2.tar.gz (42.3 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.2-py3-none-any.whl (45.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: lasty-0.1.2.tar.gz
  • Upload date:
  • Size: 42.3 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.2.tar.gz
Algorithm Hash digest
SHA256 42df3f5adc799f7f399aafa3cea8858978eb2ddc34fce156839d3dffb18ad5d7
MD5 5ace20c9cf5a3ecbc36d69587f4e0172
BLAKE2b-256 e92e654fa74085c00f35a9ff0189227fdfd9fc8bdf7a1edb9ffd09429541d9ee

See more details on using hashes here.

File details

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

File metadata

  • Download URL: lasty-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 45.1 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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 25b261f331b8e16fac0aa52dcd3c5758eee629e3cadbb964509a94e578150ddd
MD5 57e4fd8d2361fef201cf8bd66c7dd810
BLAKE2b-256 9e7caf861f041be10a1d0244e332dbc0788319bd672610f0dbbf0e7f32d222e8

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