Simple, composable RSS feed generation for Python. Framework-agnostic, works with TSV/CSV/JSON, follows Unix philosophy.
Project description
dbbasic-rss
Simple, composable RSS feed generation for Python. Framework-agnostic, works with TSV/CSV/JSON, follows Unix philosophy.
Features
- Simple Day-1 Usage: One-liner to generate RSS feeds
- Framework Agnostic: Works with Flask, Django, FastAPI, static sites, or plain Python
- Multiple Data Sources: TSV, CSV, JSON, Python lists, or directories of files
- No Heavy Dependencies: Pure stdlib for core functionality
- Composable: Works naturally with
dbbasic-tsvand other modules - Standards Compliant: Generates valid RSS 2.0 feeds
- Git-Friendly: Generate static RSS files to commit
- Type-Safe: Full type hints for better IDE support
Installation
pip install dbbasic-rss
Optional dependencies:
# For TSV support with dbbasic-tsv
pip install dbbasic-rss[tsv]
# For YAML frontmatter in from_directory()
pip install dbbasic-rss[yaml]
# For development
pip install dbbasic-rss[dev]
Quick Start
One-Liner
import dbbasic_rss as rss
# Auto-detects format and generates feed
rss.generate('articles.tsv', 'feed.xml',
title='My Blog',
url_pattern='https://example.com/{slug}/')
From TSV File
import dbbasic_rss as rss
feed = rss.from_tsv(
'articles.tsv',
title='My Blog',
link='https://example.com',
description='Articles about Python and web development',
url_pattern='https://example.com/{slug}/'
)
# Save to file
feed.write('feed.xml')
# Or get as string
xml = feed.to_xml()
print(xml)
From Python List
import dbbasic_rss as rss
posts = [
{
'title': 'Getting Started with Python',
'date': '2025-10-19',
'content': 'Learn the basics...',
'slug': 'getting-started-python',
'author': 'Dan Quellhorst'
},
{
'title': 'Advanced Python Tips',
'date': '2025-10-18',
'content': 'Expert techniques...',
'slug': 'advanced-python-tips'
}
]
feed = rss.from_posts(
posts,
title='Python Blog',
link='https://example.com',
description='Learn Python programming',
url_pattern='https://example.com/posts/{slug}/'
)
feed.write('feed.xml')
Manual Feed Building
import dbbasic_rss as rss
# Create feed
feed = rss.Feed(
title='My Tech Blog',
link='https://example.com',
description='Articles about software engineering',
language='en',
author='Dan Quellhorst',
author_email='dan@example.com'
)
# Add posts
feed.add_post(
title='Understanding RSS Feeds',
link='https://example.com/understanding-rss/',
description='A comprehensive guide to RSS',
content='<p>Full HTML content here...</p>',
pub_date='2025-10-19',
author='Dan Quellhorst',
categories=['rss', 'web', 'tutorial']
)
feed.add_post(
title='Python Web Frameworks',
link='https://example.com/python-frameworks/',
description='Comparing Flask, Django, and FastAPI',
pub_date='2025-10-18',
categories=['python', 'web']
)
# Generate XML
xml = feed.to_xml()
# Or write to file
feed.write('feed.xml')
Framework Integration
Flask
from flask import Flask, Response
import dbbasic_rss as rss
app = Flask(__name__)
@app.route('/rss')
def rss_feed():
# Load your posts data
posts = load_posts() # Your data loading logic
feed = rss.from_posts(
posts,
title='My Blog',
link='https://example.com',
description='Latest blog posts',
url_pattern='https://example.com/{slug}/'
)
return Response(feed.to_xml(), mimetype='application/rss+xml')
Django
from django.http import HttpResponse
import dbbasic_rss as rss
def rss_feed(request):
# Query your models
articles = Article.objects.all().order_by('-published_date')[:20]
# Convert to list of dicts
posts = list(articles.values('title', 'slug', 'content', 'published_date', 'author__name'))
feed = rss.from_posts(
posts,
title='My Django Blog',
link='https://example.com',
description='Latest articles',
url_pattern='https://example.com/articles/{slug}/',
date_field='published_date',
author_field='author__name'
)
return HttpResponse(feed.to_xml(), content_type='application/rss+xml')
FastAPI
from fastapi import FastAPI
from fastapi.responses import Response
import dbbasic_rss as rss
app = FastAPI()
@app.get('/rss', response_class=Response)
async def rss_feed():
posts = await get_posts() # Your async data loading
feed = rss.from_posts(
posts,
title='FastAPI Blog',
link='https://example.com',
description='Latest posts',
url_pattern='https://example.com/posts/{id}/'
)
return Response(content=feed.to_xml(), media_type='application/rss+xml')
Static Site Generator
import dbbasic_rss as rss
# Generate from markdown files
feed = rss.from_directory(
'content/posts/',
pattern='*.md',
extract_metadata=True, # Reads YAML frontmatter
title='My Static Blog',
link='https://example.com',
description='Static blog posts',
url_pattern='https://example.com/posts/{stem}/'
)
# Write static file
feed.write('public/rss.xml')
Data Sources
From TSV
# articles.tsv:
# title slug date description author
# Post 1 post-1 2025-10-19 Description... Dan
feed = rss.from_tsv(
'articles.tsv',
title='Blog',
url_pattern='https://example.com/{slug}/'
)
From CSV
feed = rss.from_csv(
'posts.csv',
title='Blog',
url_pattern='https://example.com/{id}/'
)
From JSON
# posts.json:
# [
# {"title": "Post 1", "url": "https://...", "date": "2025-10-19"},
# ...
# ]
feed = rss.from_json(
'posts.json',
title='Blog',
link='https://example.com'
)
From Directory of Markdown Files
# Reads markdown files with YAML frontmatter:
# ---
# title: My Post
# date: 2025-10-19
# author: Dan
# ---
# Content here...
feed = rss.from_directory(
'posts/',
pattern='*.md',
extract_metadata=True,
title='Blog',
url_pattern='https://example.com/{stem}/'
)
Advanced Features
Custom Field Mapping
posts = [
{
'headline': 'My Article',
'published': '2025-10-19',
'body': 'Article content...',
'permalink': 'https://example.com/articles/my-article'
}
]
feed = rss.from_posts(
posts,
title_field='headline',
date_field='published',
content_field='body',
url_field='permalink'
)
Categories/Tags
posts = [
{
'title': 'Python Tutorial',
'url': 'https://example.com/tutorial',
'tags': 'python,tutorial,beginner' # Comma-separated
}
]
feed = rss.from_posts(
posts,
categories_field='tags'
)
HTML Content with Separate Description
feed.add_post(
title='Article Title',
link='https://example.com/article',
description='Short plain-text description for RSS readers',
content='<p>Full <strong>HTML</strong> content for readers that support it</p>',
pub_date='2025-10-19'
)
Feed Image/Logo
feed = rss.Feed(
title='My Blog',
link='https://example.com',
description='Blog description',
image_url='https://example.com/logo.png',
image_title='My Blog Logo',
image_link='https://example.com'
)
Podcast Support (Enclosures)
feed.add_post(
title='Episode 1: Introduction',
link='https://example.com/podcast/ep1',
description='In this episode...',
pub_date='2025-10-19',
enclosure={
'url': 'https://example.com/audio/ep1.mp3',
'type': 'audio/mpeg',
'length': '12345678' # Size in bytes
}
)
Feed Validation
feed = rss.Feed(title='', link='https://example.com')
warnings = feed.validate()
for warning in warnings:
print(f"Warning: {warning}")
# Output: Warning: Feed title is missing
Feed Statistics
print(f"Total posts: {feed.count()}")
print(f"Oldest post: {feed.oldest()}")
print(f"Newest post: {feed.newest()}")
Working with dbbasic-tsv
from dbbasic_tsv import TSV
import dbbasic_rss as rss
# Read and query with dbbasic-tsv
db = TSV('articles.tsv')
posts = db.query(category='python', limit=10)
# Generate RSS feed
feed = rss.from_posts(
posts,
title='Python Articles',
url_pattern='https://example.com/{slug}/'
)
feed.write('python-feed.xml')
API Reference
Feed(title, link, description, ...)
Main feed class.
Parameters:
title(str): Feed titlelink(str): Feed URLdescription(str): Feed descriptionlanguage(str): Language code (default: 'en')author(str): Author nameauthor_email(str): Author emailimage_url(str): Feed image/logo URLttl(int): Cache time-to-live in minutescategory(str): Feed category
Methods:
add_post(**kwargs): Add a post to the feedto_xml(): Generate RSS XML stringwrite(filepath): Write feed to filevalidate(): Return list of validation warningscount(): Return number of itemsoldest(): Return oldest publication datenewest(): Return newest publication date
from_posts(posts, ...)
Generate feed from list of dictionaries.
Parameters:
posts(list): List of post dictionariestitle(str): Feed titlelink(str): Feed URLdescription(str): Feed descriptionurl_pattern(str): URL pattern with {field} placeholderstitle_field(str): Field name for title (default: 'title')date_field(str): Field name for date (default: 'date')content_field(str): Field name for content (default: 'content')author_field(str): Field name for author (default: 'author')url_field(str): Field name for URL (default: 'url')categories_field(str): Field name for categories**kwargs: Additional Feed() arguments
from_tsv(filepath, ...), from_csv(filepath, ...), from_json(filepath, ...)
Generate feed from file. Same parameters as from_posts().
from_directory(directory, pattern='*.md', ...)
Generate feed from directory of files.
Additional Parameters:
directory(str): Directory pathpattern(str): Glob pattern (default: '*.md')extract_metadata(bool): Extract YAML frontmatter (default: True)
generate(source, output, ...)
One-liner to generate feed from file to file.
Parameters:
source(str): Input file path (.tsv, .csv, .json)output(str): Output XML file path**kwargs: Same asfrom_posts()
Why dbbasic-rss?
Unix Philosophy
- Do one thing well: Generate RSS feeds
- Plain text: Works with TSV, CSV, JSON files
- Composable: Pipes data through functions
- No vendor lock-in: Framework-agnostic
Comparison to Other Libraries
feedgen (most popular):
- ❌ Heavy API, steep learning curve
- ❌ Framework-specific patterns
- ❌ Complex for simple use cases
dbbasic-rss:
- ✅ One-liner for 80% of use cases
- ✅ Works with any framework
- ✅ Composable with other tools
- ✅ Educational (shows how RSS works)
Teaching Tool
RSS is just XML! dbbasic-rss helps you understand RSS by providing:
- Clear, readable code
- Simple templates (no magic)
- Standards compliance
- Incremental learning (simple → advanced)
Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass:
pytest - Submit a pull request
License
MIT License - see LICENSE file for details.
Links
- GitHub: https://github.com/askrobots/dbbasic-rss
- PyPI: https://pypi.org/project/dbbasic-rss/
- Documentation: https://github.com/askrobots/dbbasic-rss#readme
- Issues: https://github.com/askrobots/dbbasic-rss/issues
Credits
Created by Ask Robots as part of the DBBasic framework.
Inspired by the Unix philosophy and built for simplicity, composability, and ease of learning.
Project details
Release history Release notifications | RSS feed
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 dbbasic_rss-1.0.0.tar.gz.
File metadata
- Download URL: dbbasic_rss-1.0.0.tar.gz
- Upload date:
- Size: 17.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4bfd121c55466ca6d1d66053c76ac3b1339b4d66ce18a854d005ef9d19c9da15
|
|
| MD5 |
4dceb5388adcdee60ac92addc2c63d5b
|
|
| BLAKE2b-256 |
5d685a4669c7e7e291d29ffb8cecee66833fa9db2afa7e0ff9aeb117410b02b1
|
File details
Details for the file dbbasic_rss-1.0.0-py3-none-any.whl.
File metadata
- Download URL: dbbasic_rss-1.0.0-py3-none-any.whl
- Upload date:
- Size: 13.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5017b3f3774da5e35887b86a2c6ddf4080a9f2cc6acef7426d9e625a4b613a26
|
|
| MD5 |
193b7a57b854a0cde5701fe6df6a4f8c
|
|
| BLAKE2b-256 |
b9c4335aa9ee10641b5cdff32cf8f50d2a7d614ce9b4284250bf706886a9549d
|