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.
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
aiohttpfor 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
99665091a7fcfc5269073fa969f3eadae72717690bc8a12fc299966f3f53a9bc
|
|
| MD5 |
ac9464248fec10a5e8d5526e507ce49c
|
|
| BLAKE2b-256 |
d24a1276f4e479e711d6fec9f1b0c941230cd83ca1ce8151d8a819ee6cd6adcd
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c5a7f19a84a109404076ebb83c5df3a0f076ca83d125468dd1c99275559617e0
|
|
| MD5 |
57183093d87e77c7fe766b81f6021742
|
|
| BLAKE2b-256 |
3a2ebbabe4e521b287e9d6f055d2633897231451c5ccd09a99d0b09c4cba6119
|