Skip to main content

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-tsv and 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 title
  • link (str): Feed URL
  • description (str): Feed description
  • language (str): Language code (default: 'en')
  • author (str): Author name
  • author_email (str): Author email
  • image_url (str): Feed image/logo URL
  • ttl (int): Cache time-to-live in minutes
  • category (str): Feed category

Methods:

  • add_post(**kwargs): Add a post to the feed
  • to_xml(): Generate RSS XML string
  • write(filepath): Write feed to file
  • validate(): Return list of validation warnings
  • count(): Return number of items
  • oldest(): Return oldest publication date
  • newest(): Return newest publication date

from_posts(posts, ...)

Generate feed from list of dictionaries.

Parameters:

  • posts (list): List of post dictionaries
  • title (str): Feed title
  • link (str): Feed URL
  • description (str): Feed description
  • url_pattern (str): URL pattern with {field} placeholders
  • title_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 path
  • pattern (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 as from_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:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass: pytest
  5. Submit a pull request

License

MIT License - see LICENSE file for details.

Links

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


Download files

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

Source Distribution

dbbasic_rss-1.0.0.tar.gz (17.9 kB view details)

Uploaded Source

Built Distribution

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

dbbasic_rss-1.0.0-py3-none-any.whl (13.3 kB view details)

Uploaded Python 3

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

Hashes for dbbasic_rss-1.0.0.tar.gz
Algorithm Hash digest
SHA256 4bfd121c55466ca6d1d66053c76ac3b1339b4d66ce18a854d005ef9d19c9da15
MD5 4dceb5388adcdee60ac92addc2c63d5b
BLAKE2b-256 5d685a4669c7e7e291d29ffb8cecee66833fa9db2afa7e0ff9aeb117410b02b1

See more details on using hashes here.

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

Hashes for dbbasic_rss-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5017b3f3774da5e35887b86a2c6ddf4080a9f2cc6acef7426d9e625a4b613a26
MD5 193b7a57b854a0cde5701fe6df6a4f8c
BLAKE2b-256 b9c4335aa9ee10641b5cdff32cf8f50d2a7d614ce9b4284250bf706886a9549d

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