Lightweight library to handle data and reproduce workflows
Project description
Penwings
Penwings is a lightweight Python library for building reproducible data workflows.
It provides simple, composable tools to:
- manage project structure
- standardize data access
- cache SQL queries efficiently
The goal is to reduce boilerplate and make data pipelines faster, cleaner, and reproducible by default.
✨ Features
🗂️ Project Structure Management
- Standardized folder setup for data science projects
- Automatic project root detection
- Flexible, extensible path system
🧠 SQL → Parquet Caching
- Execute SQL queries via SQLAlchemy
- Automatically cache results as Parquet
- Reuse cached data to avoid unnecessary database hits
- Configurable refresh logic
⚡ Lightweight & Modular
- Minimal core dependencies (
pandas,numpy) - Optional SQL support
- Designed to scale into larger workflows
📦 Installation
pip install penwings
Optional SQL support
pip install penwings[sql]
Requires Python 3.11+
🚀 Quick Start
1. Project structure
from penwings import ProjectPaths
paths = ProjectPaths()
print(paths.data)
print(paths.models)
Creates a standardized structure like:
configs/
data/
raw/
processed/
external/
features/
logs/
models/
notebooks/
reports/
figures/
tables/
sql/
2. SQL caching
from sqlalchemy import create_engine
from penwings import SQLParquetCache
engine = create_engine("sqlite:///example.db")
cache = SQLParquetCache(
sql_dir="sql",
parquet_dir="cache",
conn=engine,
refresh_days=1
)
📊 Usage
Using SQL files
df = cache.get("sales.sql")
- Loads from Parquet if cached
- Otherwise executes SQL and caches result
Using raw SQL
query = "SELECT * FROM sales WHERE month = '2026-02'"
df = cache.get(
sql=query,
parquet_name="sales_feb"
)
Cache behavior
df = cache.get("sales.sql", force=True)
-
force=True→ always re-run SQL -
refresh_days=N→ cache expires after N days -
returns:
DataFrame
🧩 ProjectPaths
Create only specific parts of a project:
paths = ProjectPaths(folders=["data", "ml"])
Custom directories:
paths = ProjectPaths(
custom_dirs={
"modules": "src/modules",
"views": "src/views"
}
)
Access paths:
paths.data
paths["models"]
paths.as_dict()
🧠 Design Philosophy
Penwings is built around a few core ideas:
- Reproducibility first → deterministic data access via caching
- Convention over configuration → sensible defaults for structure
- Composable building blocks → small tools that work well together
- Lightweight core → no heavy framework overhead
🛣️ Roadmap
- Pipeline abstraction (data workflows as steps)
- Improved SQL utilities and query management
- Integration with feature engineering workflows
- Better caching strategies and metadata tracking
🔢 Versioning
Penwings follows semantic versioning:
- MAJOR → breaking changes
- MINOR → new features
- PATCH → bug fixes
🤝 Contributing
Contributions are welcome!
- Fork the repository
- Create a branch (
feature/my-feature) - Commit your changes
- Open a pull request
📄 License
MIT License — see LICENSE
💡 Example Workflow
from sqlalchemy import create_engine
from penwings import ProjectPaths, SQLParquetCache
# Setup project structure
paths = ProjectPaths()
# Setup SQL cache
engine = create_engine("sqlite:///example.db")
cache = SQLParquetCache(
sql_dir=paths.sql,
parquet_dir=paths.data,
conn=engine
)
# Load data
df_sales = cache.get("sales.sql")
⭐ Why Penwings?
Penwings sits between:
- ad-hoc scripts ❌
- heavy frameworks ❌
It gives you just enough structure to:
- stay organized
- move fast
- keep workflows reproducible
without getting in your way.
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 penwings-0.3.0.dev1.tar.gz.
File metadata
- Download URL: penwings-0.3.0.dev1.tar.gz
- Upload date:
- Size: 49.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"43","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ff09c7fe52ef4b6e1972b870f8b1055bfff049825da1aa4cc8e339b9bbab6a5
|
|
| MD5 |
9e90676b10f0b1729fe67f71318fcdab
|
|
| BLAKE2b-256 |
829098ea337be6836fdeb2c5e1966a22d00c233b7bf4daba17b0b82ea1016ac0
|
File details
Details for the file penwings-0.3.0.dev1-py3-none-any.whl.
File metadata
- Download URL: penwings-0.3.0.dev1-py3-none-any.whl
- Upload date:
- Size: 13.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"43","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
46907784c94176f7953775eb341d9e7eec15eedac32dd7fa5aa3bfc4b13b1b5a
|
|
| MD5 |
eceb8e81d7580c900b7955f059e6597e
|
|
| BLAKE2b-256 |
3c5eec16f00a4e62b63dbdccb7166ac6f56a0c633155afc3c9b065a4b8bf4c91
|