Skip to main content

Intelligent rule-based function dispatch for Python

Project description

SmartSwitch Logo

SmartSwitch

Intelligent rule-based function dispatch for Python

PyPI version Tests codecov Python 3.10+ License: MIT Documentation


📚 Part of the genro-libs family General-purpose Python developer tools

Replace messy if-elif chains and duplicated logic with clean, maintainable function registries.

Installation

pip install smartswitch

The Problem-Solution Approach

SmartSwitch helps you organize code that needs to handle different cases. Let's see how, step by step.

1. Function Registry Pattern

Problem: You have several operations and want to call them by name.

Traditional approach - Dictionary of functions:

# Hard to maintain, easy to make mistakes
operations = {
    'save': save_data,
    'load': load_data,
    'delete': delete_data
}

# Calling
op = operations.get(action)
if op:
    op(data)

With SmartSwitch - Clean registration:

from smartswitch import Switcher

ops = Switcher()

@ops
def save_data(data):
    # Save logic
    pass

@ops
def load_data(data):
    # Load logic
    pass

@ops
def delete_data(data):
    # Delete logic
    pass

# Call by name
ops('save_data')(data)

2. Custom Action Names

Problem: You want friendly names different from function names.

Traditional approach - Manual mapping:

actions = {
    'reset': destroy_all_data,
    'clear': remove_cache,
    'wipe': erase_history
}

action = actions[command]
action()

With SmartSwitch - Alias registration:

ops = Switcher()

@ops('reset')
def destroy_all_data():
    pass

@ops('clear')
def remove_cache():
    pass

# Call with alias
ops('reset')()

Or use prefix-based auto-naming for convention-driven naming:

# Set a prefix to auto-derive handler names
protocols = Switcher(prefix='protocol_')

@protocols  # Auto-registers as 's3_aws'
def protocol_s3_aws():
    return {"type": "s3", "region": "us-east-1"}

@protocols  # Auto-registers as 'gcs'
def protocol_gcs():
    return {"type": "gcs", "bucket": "data"}

# Call by auto-derived names
protocols('s3_aws')()
protocols('gcs')()

3. Value-Based Dispatch

Problem: Choose handler based on actual data values.

Traditional approach - Long if-elif chains:

def process_user(user_type, reason):
    if user_type == 'to_delete' and reason == 'no_payment':
        # Remove user
        pass
    elif reason == 'no_payment':
        # Send reminder
        pass
    elif user_type == 'to_delete':
        # Archive
        pass
    else:
        # Default
        pass

With SmartSwitch - Declarative rules:

users = Switcher()

@users(valrule=lambda user_type, reason:
       user_type == 'to_delete' and reason == 'no_payment')
def remove_user(user_type, reason):
    # Remove user
    pass

@users(valrule=lambda reason: reason == 'no_payment')
def send_payment_reminder(user_type, reason):
    # Send reminder
    pass

@users(valrule=lambda user_type: user_type == 'to_delete')
def archive_user(user_type, reason):
    # Archive
    pass

@users
def handle_default(user_type, reason):
    # Default
    pass

# Automatic dispatch
users()(user_type='to_delete', reason='no_payment')

Tip: For multi-parameter conditions, you can use compact dict-style lambda:

@users(valrule=lambda kw: kw['user_type'] == 'to_delete' and kw['reason'] == 'no_payment')
def remove_user(user_type, reason):
    pass

4. Type-Based Dispatch

Problem: Handle different data types differently.

Traditional approach - Multiple isinstance checks:

def process(data):
    if isinstance(data, str):
        return data.upper()
    elif isinstance(data, int):
        return data * 2
    elif isinstance(data, list):
        return len(data)
    else:
        return None

With SmartSwitch - Type rules:

processor = Switcher()

@processor(typerule={'data': str})
def process_string(data):
    return data.upper()

@processor(typerule={'data': int})
def process_number(data):
    return data * 2

@processor(typerule={'data': list})
def process_list(data):
    return len(data)

@processor
def process_other(data):
    return None

# Automatic dispatch based on type
processor()(data="hello")  # → HELLO
processor()(data=42)       # → 84

Real-World Examples

API Routing

api = Switcher()

@api(valrule=lambda method, path: method == 'GET' and path == '/users')
def get_users(method, path, data=None):
    return list_all_users()

@api(valrule=lambda method, path: method == 'POST' and path == '/users')
def create_user(method, path, data=None):
    return create_new_user(data)

@api
def not_found(method, path, data=None):
    return {"error": "Not Found", "status": 404}

# Dispatch
response = api()('GET', '/users')

Payment Processing

payments = Switcher()

@payments(typerule={'amount': int | float},
          valrule=lambda method, amount: method == 'crypto' and amount > 1000)
def process_large_crypto(method, amount, details):
    return {"processor": "crypto_large", "fee": amount * 0.01}

@payments(valrule=lambda method, **kw: method == 'credit_card')
def process_card(method, amount, details):
    return {"processor": "credit_card", "fee": amount * 0.03}

@payments
def process_generic(method, amount, details):
    return {"error": "Unsupported payment method"}

When to Use

Good for:

  • API handlers and request routers
  • Business logic with multiple branches
  • Plugin systems and extensible architectures
  • State machines and workflow engines
  • When you need type + value checks together

⚠️ Consider alternatives for:

  • Simple 2-3 case switches → use if/elif
  • Pure type dispatch → use functools.singledispatch
  • Very high-performance code (< 10μs functions called millions of times)

Key Features

Core Dispatch Mechanisms

Handler Management

Developer Experience

  • 🧩 Modular & testable: Each handler is an independent function
  • Clean API: Pythonic decorators with zero boilerplate
  • 🚀 Efficient: Optimized with caching (~1-2μs overhead)
  • 🛡️ Type-safe: Full type annotation support

Documentation

📚 Full documentation: smartswitch.readthedocs.io

Guides:

All examples in documentation are tested - They come directly from our test suite with 95% coverage.

Performance

SmartSwitch adds ~1-2 microseconds per dispatch. For real-world functions (API calls, DB queries, business logic), this overhead is negligible:

Function time: 50ms (API call)
Dispatch overhead: 0.002ms
Impact: 0.004% ✅

See Performance Best Practices for more details.

License

MIT

Contributing

Contributions welcome! Please feel free to submit a Pull Request.

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

smartswitch-0.2.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.

smartswitch-0.2.0-py3-none-any.whl (9.4 kB view details)

Uploaded Python 3

File details

Details for the file smartswitch-0.2.0.tar.gz.

File metadata

  • Download URL: smartswitch-0.2.0.tar.gz
  • Upload date:
  • Size: 17.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for smartswitch-0.2.0.tar.gz
Algorithm Hash digest
SHA256 f2260bef3cd6632133c3cda485a5221b4a838e91c8fed85f0a904a9e5d4d9be2
MD5 a632e05607129ff0802fcfb2a336e289
BLAKE2b-256 05d625ba9738b40079b11cb7b0426a25bd2ee3fa6057f1c4f96ba9ab39c95172

See more details on using hashes here.

Provenance

The following attestation bundles were made for smartswitch-0.2.0.tar.gz:

Publisher: publish.yml on genropy/smartswitch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file smartswitch-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: smartswitch-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 9.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for smartswitch-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 97095bbae7854ffeb4e59c271ffbe5f7c0c5c45aac5f54aae79f93f97f52f7db
MD5 b3d4f631873a3fdb3a1ad2e490fe222e
BLAKE2b-256 7f558ba7d0ce572fb80296972cb4cebe141bdbbad3497dee9e9462401a30836e

See more details on using hashes here.

Provenance

The following attestation bundles were made for smartswitch-0.2.0-py3-none-any.whl:

Publisher: publish.yml on genropy/smartswitch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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