Skip to main content

Production-grade currency conversion with smart error handling, encrypted caching, and fuzzy locale matching

Project description

Robust Currency Conversion Solution

Python 3.11+ License: MIT Code style: black

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() or print()
  • ๐Ÿง  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_records limit (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

๐Ÿ“ž Support


Made with โค๏ธ for developers who need reliable currency handling

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

pypenny-0.1.0.tar.gz (37.9 kB view details)

Uploaded Source

Built Distribution

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

pypenny-0.1.0-py3-none-any.whl (29.7 kB view details)

Uploaded Python 3

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

Hashes for pypenny-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b795671fd5cf1c906db66ac31e8dac7509f1d7342299262c4b486998332429e0
MD5 45432a8ba17d3e8d251f1f013fda05ff
BLAKE2b-256 263c7e20b3d99752b6268bde8ec0b011a9a7d2912017c849b3ff20b62267e327

See more details on using hashes here.

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

Hashes for pypenny-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 17b621806f7d5f859959a447c3f0628b94df1fa738453a9027dbbd86fa23287e
MD5 d7e9c3ef318315a9729234080de9bf6a
BLAKE2b-256 f0f1c65d4101a8bd6aac7b56fb7f2250ef3b6c96a22550e6b612137e836fe17e

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