Production-grade currency conversion with smart error handling, encrypted caching, and fuzzy locale matching
Project description
Robust Currency Conversion Solution
Production-grade currency conversion library with smart error handling, encrypted caching, and fuzzy locale matching.
โจ Features
- ๐ฏ Professional DX: Safe string operations, no crashes with
str()orprint() - ๐ง Smart Error Handling: Fuzzy locale matching auto-corrects typos (
em_USโen_US) - โ๏ธ Flexible Configuration: Control cache behavior and restrict allowed currencies
- ๐ Encrypted Cache: Fernet (AES-128) encryption with cross-platform storage
- ๐ Deduplication Logic: Only 1 record per day if exchange rate unchanged
- ๐ 180+ Currencies: Supports all ISO 4217 currencies via py-moneyed
- ๐ Dual Exchange Strategies: Live network-based + encrypted local cache fallback
- ๐ฆ Zero Config: Works out of the box with sensible defaults
๐ Quick Start
Installation
# Using uv (recommended)
uv pip install robust-currency
# Using pip
pip install robust-currency
Basic Usage
from config import CurrencyConfig
from currency_manager import CurrencyManager
# Create configuration
config = CurrencyConfig(application_name="MyApp")
manager = CurrencyManager(config)
# Create money
money_usd = manager.create_money('100', 'USD')
# Convert
money_egp = manager.convert(money_usd, 'EGP')
# Format (safe to print!)
print(manager.format(money_egp, locale='ar_EG'))
# Output: โ4,752.00 ุฌ.ู
.โ
Strict Configuration (Production)
config = CurrencyConfig(
application_name="ProductionApp",
allow_cache_fallback=False, # Fail fast on network errors
allowed_currencies=['USD', 'EGP'], # Whitelist only
cache_max_records=10,
cache_retention_days=7
)
manager = CurrencyManager(config)
# This works
money_usd = manager.create_money('100', 'USD')
# This raises CurrencyNotAllowedError with helpful message
money_eur = manager.create_money('100', 'EUR')
# Error: Currency 'EUR' not allowed. Allowed: USD, EGP.
# Update config.allowed_currencies to include 'EUR'.
๐ Key Features
1. Fuzzy Locale Matching
Automatically corrects common locale mistakes:
manager.format(money, locale='EN_us') # โ Normalized to 'en_US'
manager.format(money, locale='US_EN') # โ Swapped to 'en_US'
manager.format(money, locale='em_US') # โ Typo corrected to 'en_US'
manager.format(money, locale='US') # โ Alias resolved to 'en_US'
2. Smart Error Messages
# Invalid currency with suggestions
manager.create_money('100', 'USDD')
# Error: Invalid currency code: 'USDD'. Did you mean: USD?
# Currency not allowed
manager.create_money('100', 'EUR') # When only USD/EGP allowed
# Error: Currency 'EUR' not allowed. Allowed: USD, EGP.
# Update config.allowed_currencies to include 'EUR'.
3. Encrypted Cache with Deduplication
# Cache automatically stores exchange rates
money_converted = manager.convert(money_usd, 'EGP')
# Get cache statistics
stats = manager.get_cache_stats()
print(stats['total_records']) # Number of cached rates
print(stats['cache_file']) # Path to encrypted cache file
# Cleanup old records
removed = manager.cleanup_cache()
Deduplication Logic:
- Same day + same rate โ Skip (no duplicate)
- Same day + different rate (>0.01%) โ Store new record
- Different day โ Always store new record
- Enforces
max_recordslimit (keeps most recent)
4. Cache File Locations (via platformdirs)
- Windows:
C:\Users\<user>\AppData\Local\<app_name>\Cache\exchange_cache.enc - Linux:
~/.cache/<app_name>/exchange_cache.enc - macOS:
~/Library/Caches/<app_name>/exchange_cache.enc
5. Exchange Strategies
# LIVE: Always fetch from network
converted = manager.convert(money, 'EGP', strategy='live')
# CACHED: Use local cache only
converted = manager.convert(money, 'EGP', strategy='cached')
# AUTO: Try live, fallback to cache (default)
converted = manager.convert(money, 'EGP', strategy='auto')
๐ง Configuration Options
CurrencyConfig(
# Required
application_name: str, # For cache directory naming
# Cache behavior
allow_cache_fallback: bool = True, # Fallback to cache on network failure
cache_max_records: int = 7, # Max records per currency pair
cache_retention_days: int = 3, # Days to retain cached records
cache_file_path: str = None, # Custom cache path (None = platformdirs)
# Currency restrictions
allowed_currencies: List[str] = None, # Whitelist (None = all allowed)
# Exchange settings
default_exchange_strategy: str = 'auto', # 'live', 'cached', 'auto'
default_locale: str = 'en_US',
# API settings
api_key: str = None,
api_timeout: int = 5,
api_max_retries: int = 3,
)
๐งช Testing
# Install dev dependencies
uv pip install -e ".[dev]"
# Run tests
pytest
# Run with coverage
pytest --cov=. --cov-report=html
# Run stress tests
pytest tests/test_stress.py -v
๐ Advanced Usage
Safe Operations
# All these are safe (no crashes!)
money = manager.create_money('100.50', 'USD')
print(manager.format(money)) # Safe
formatted = f"{manager.format(money)}" # Safe in f-strings
# Lists of money
money_list = [
manager.create_money('10', 'USD'),
manager.create_money('20', 'USD'),
]
formatted_list = [manager.format(m) for m in money_list]
print(formatted_list) # Safe
Currency Operations
# Addition (same currency only)
total = manager.add(
manager.create_money('50', 'USD'),
manager.create_money('25', 'USD')
)
# Subtraction
difference = manager.subtract(
manager.create_money('100', 'USD'),
manager.create_money('25', 'USD')
)
# Attempting to add different currencies raises clear error
manager.add(money_usd, money_egp)
# Error: Cannot perform addition on different currencies: USD and EGP.
# Convert to the same currency first.
๐๏ธ Architecture
โโโโโโโโโโโโโโโโโโโ
โ CurrencyManager โ โ Main user-facing API
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโดโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโ
โผ โผ โผ โผ
โโโโโโโโโโ โโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโโ
โ Config โ โLocaleโ โExchange โ โEncryptionโ
โ โ โMatch โ โ Cache โ โ Utils โ
โโโโโโโโโโ โโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโโ
๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
- Built on py-moneyed for robust currency handling
- Uses Babel for locale-aware formatting
- Encryption via cryptography
- Cross-platform paths via platformdirs
๐ Support
- ๐ Documentation
- ๐ Issue Tracker
- ๐ฌ Discussions
Made with โค๏ธ for developers who need reliable currency handling
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 pypenny-0.1.0.tar.gz.
File metadata
- Download URL: pypenny-0.1.0.tar.gz
- Upload date:
- Size: 37.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.17
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b795671fd5cf1c906db66ac31e8dac7509f1d7342299262c4b486998332429e0
|
|
| MD5 |
45432a8ba17d3e8d251f1f013fda05ff
|
|
| BLAKE2b-256 |
263c7e20b3d99752b6268bde8ec0b011a9a7d2912017c849b3ff20b62267e327
|
File details
Details for the file pypenny-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pypenny-0.1.0-py3-none-any.whl
- Upload date:
- Size: 29.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.17
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17b621806f7d5f859959a447c3f0628b94df1fa738453a9027dbbd86fa23287e
|
|
| MD5 |
d7e9c3ef318315a9729234080de9bf6a
|
|
| BLAKE2b-256 |
f0f1c65d4101a8bd6aac7b56fb7f2250ef3b6c96a22550e6b612137e836fe17e
|