Smart Publisher - CLI/API framework based on SmartSwitch
Project description
smpub - Smart Publisher
CLI/API framework based on SmartSwitch
Build CLI and API applications with automatic command dispatch using SmartSwitch.
What is smpub?
The Problem
When you write a Python library, you typically need to provide multiple interfaces:
- Pythonic API - Import and use directly in code
- CLI interface - Command-line usage for scripts and users
- HTTP/API - Web access, integrations, remote calls
Traditionally, this means writing three different interfaces with lots of boilerplate code.
The Solution
smpub (Smart Publisher) offers an elegant approach:
- Write your library once using SmartSwitch for method dispatch
- Get three interfaces automatically: Python, CLI, and HTTP/API
SmartSwitch provides an elegant Pythonic dispatch system using decorators. smpub takes that dispatch system and automatically transforms it into CLI commands and HTTP endpoints.
Key Concept
Pythonic dispatch (SmartSwitch) → Automatic CLI + HTTP (smpub)
One codebase, three interfaces:
# 1. Your library (uses SmartSwitch for elegant dispatch)
from smartswitch import Switcher
class MyService:
api = Switcher(prefix='my_')
@api
def my_operation(self, param: str):
"""Process a parameter."""
return {"result": param}
# 2. Publishing layer (uses smpub) - just ~20 lines!
from smartpublisher import Publisher
class MyApp(Publisher):
def on_init(self):
self.publish("service", MyService())
Result: Your service is now accessible three ways:
# Python API (direct import)
from myapp import MyService
service = MyService()
service.my_operation("test")
# CLI (automatic)
python myapp.py service operation test
# HTTP API (automatic)
curl http://localhost:8000/service/operation -d '{"param": "test"}'
# Plus OpenAPI/Swagger at /docs
Why SmartSwitch?
SmartSwitch provides an elegant Pythonic dispatch system with:
- Clean decorator syntax (
@api) - Plugin chain for cross-cutting concerns (logging, validation, transactions)
- Type-safe method routing
- Composable behavior
When you use SmartSwitch, your code is already well-structured for dispatch. smpub simply transforms that dispatch into multiple interfaces.
Learn more: See how a real application uses SmartSwitch plugins in the Demo Shop documentation (SQL database with transaction management, validation, and format negotiation).
Features
- 🎯 Publisher Pattern - Register handlers and expose them via CLI/API
- 🔀 SmartSwitch Integration - Rule-based function dispatch
- 💻 CLI Generation - Automatic command-line interface
- ✅ Pydantic Validation - Automatic type validation and conversion
- 🎨 Interactive Mode - Optional Textual TUI for parameter prompting
- 🌐 HTTP/API Mode - FastAPI with OpenAPI/Swagger UI
- 📝 Registry System - Local/global app registration
Installation
pip install smartpublisher
# With HTTP support
pip install smartpublisher[http]
Quick Start
Workflow
1. Write your code with SmartSwitch → 2. Create Publisher → 3. Get CLI + HTTP API
1. Write Your Service (with SmartSwitch)
from typing import Literal
from smartswitch import Switcher
class AccountHandler:
# If using __slots__, include 'smpublisher'
__slots__ = ('accounts', 'smpublisher')
api = Switcher(prefix='account_')
def __init__(self):
self.accounts = {}
@api
def account_add(self, name: str, smtp_host: str, smtp_port: int = 587,
username: str = "", use_tls: bool = True,
auth_method: Literal["plain", "login", "oauth2"] = "plain"):
"""Add a new mail account."""
self.accounts[name] = {"smtp_host": smtp_host, "smtp_port": smtp_port,
"username": username, "use_tls": use_tls}
return {"success": True, "account": self.accounts[name]}
@api
def account_list(self):
"""List all accounts."""
return {"count": len(self.accounts), "accounts": list(self.accounts.values())}
class MailHandler:
# If using __slots__, include 'smpublisher'
__slots__ = ('account_handler', 'messages', 'smpublisher')
api = Switcher(prefix='mail_')
def __init__(self, account_handler):
self.account_handler = account_handler
self.messages = []
@api
def mail_send(self, account: str, to: str, subject: str, body: str,
priority: Literal["low", "normal", "high"] = "normal",
html: bool = False):
"""Send an email message."""
message = {"account": account, "to": to, "subject": subject, "body": body}
self.messages.append(message)
return {"success": True, "message_id": len(self.messages)}
2. Create Publisher (with smpub)
from smartpublisher import Publisher
class MailApp(Publisher):
def on_init(self):
self.account = AccountHandler()
self.mail = MailHandler(self.account)
# Publish handlers - that's it!
self.publish('account', self.account)
self.publish('mail', self.mail)
if __name__ == "__main__":
app = MailApp()
app.run() # Auto-detect CLI or HTTP mode
3. Use It - Direct Execution
CLI Mode (direct execution):
# Add mail account
python mailapp.py account add work smtp.gmail.com 587 user@work.com
# Send email
python mailapp.py mail send work recipient@example.com "Hello" "Message body"
# Interactive mode (prompts for parameters)
python mailapp.py mail send --interactive
HTTP Mode (automatic):
# Start server (no CLI args = HTTP mode)
python mailapp.py
# Opens Swagger UI at http://localhost:8000/docs
# Call API
curl -X POST http://localhost:8000/mail/send \
-H "Content-Type: application/json" \
-d '{"account": "work", "to": "user@example.com", "subject": "Hello", "body": "Message"}'
4. Optional: Register for Global Access
Register your app to use it from anywhere:
# Register app
smpub register mailapp ~/projects/mailapp
# Now use from anywhere
smpub run mailapp account list
smpub serve mailapp # Start HTTP server
# List registered apps
smpub list
# Unregister
smpub unregister mailapp
When to use registry?
- You have multiple apps and want to switch between them
- You want to use your app from any directory
- You're building reusable tools for your team
Documentation
Main Documentation
For complete framework documentation, visit smartpublisher.readthedocs.io.
Topics covered:
- Publisher and handler patterns
- Registry system (register/run apps)
- CLI command structure
- HTTP/API mode with FastAPI
- Type validation with Pydantic
- Interactive mode with Textual TUI
Real-World Example
For a complete example showing SmartSwitch plugins, database adapters, and advanced patterns, see:
Demo Shop Documentation - E-commerce library with:
- SQL database system with adapters (SQLite/PostgreSQL)
- Table managers with CRUD operations
- SmartSwitch plugin chain (Logging, Pydantic, DbOp)
- Transaction management
- Format negotiation (JSON, Markdown, HTML)
- Published in ~20 lines with smpub
The demo shows how a well-structured SmartSwitch application becomes trivial to publish.
Part of Genro-Libs Family
smpub is part of the Genro-Libs toolkit, a collection of general-purpose Python developer tools.
Related Projects:
- smartswitch - Rule-based function dispatch (used by smpub)
- gtext - Text transformation tool
Requirements
- Python 3.10+
- smartswitch >= 0.1.0
- pydantic >= 2.0
- textual >= 0.41.0 (optional, for interactive mode)
Development
git clone https://github.com/genropy/smartpublisher.git
cd smartpublisher
pip install -e ".[dev]"
pytest
License
MIT License - see LICENSE file for details.
Links
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 smartpublisher-0.3.0.tar.gz.
File metadata
- Download URL: smartpublisher-0.3.0.tar.gz
- Upload date:
- Size: 45.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bbf1698944a5dac696e4372a0fc52e8e43e255d0544f59a5526b5fda97935675
|
|
| MD5 |
546f9adcade98f0e4db3de8fa6bf172c
|
|
| BLAKE2b-256 |
f0965f2661406951e50fc3f50e45a0fdb34c68960a8b98acfc2c34162a29ec94
|
Provenance
The following attestation bundles were made for smartpublisher-0.3.0.tar.gz:
Publisher:
publish.yml on genropy/smartpublisher
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
smartpublisher-0.3.0.tar.gz -
Subject digest:
bbf1698944a5dac696e4372a0fc52e8e43e255d0544f59a5526b5fda97935675 - Sigstore transparency entry: 699623661
- Sigstore integration time:
-
Permalink:
genropy/smartpublisher@67302c2b95949912c62716b6f4405bc9459b614b -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/genropy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@67302c2b95949912c62716b6f4405bc9459b614b -
Trigger Event:
release
-
Statement type:
File details
Details for the file smartpublisher-0.3.0-py3-none-any.whl.
File metadata
- Download URL: smartpublisher-0.3.0-py3-none-any.whl
- Upload date:
- Size: 28.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
81bfe30ce9eca7bf7dc7854d220ea0e868f13f70a93103e4eab844bcc5f0e5dd
|
|
| MD5 |
1243215f05acf06c5c48d5acef00c521
|
|
| BLAKE2b-256 |
9921b55b1b69adfb41a31350747f0744f03b28dfb4721ed3a20e356b21867a5b
|
Provenance
The following attestation bundles were made for smartpublisher-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on genropy/smartpublisher
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
smartpublisher-0.3.0-py3-none-any.whl -
Subject digest:
81bfe30ce9eca7bf7dc7854d220ea0e868f13f70a93103e4eab844bcc5f0e5dd - Sigstore transparency entry: 699623664
- Sigstore integration time:
-
Permalink:
genropy/smartpublisher@67302c2b95949912c62716b6f4405bc9459b614b -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/genropy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@67302c2b95949912c62716b6f4405bc9459b614b -
Trigger Event:
release
-
Statement type: