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.1.tar.gz (42.2 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.1-py3-none-any.whl (45.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: lasty-0.1.1.tar.gz
  • Upload date:
  • Size: 42.2 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.1.tar.gz
Algorithm Hash digest
SHA256 9897c5dd15cac9fd435ccd91c5b89d46a6e87fc7a1e29caa10e512f787bd952b
MD5 82a79b7ac547205ec47065ac7e39c2c0
BLAKE2b-256 6cf5680a9516f0dcc74609bb6ac37f00ffba687630a675162271e622fa90e0a1

See more details on using hashes here.

File details

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

File metadata

  • Download URL: lasty-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 45.2 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 83f756e1f4df14ffe224a974be8da60908255b3126945c60d72433b3ccadc412
MD5 eb06bcf677f4fd57634883beade59ad8
BLAKE2b-256 1a31453f3101011f9ef30370148cab0b922262b15bbb33deb1e9c4cb5f96c01f

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