Skip to main content

This library provides a robust set of decorators for enhancing Python code functionality. It includes:

Project description

🎯 Python Advanced Decorators Library

A comprehensive collection of flexible and reusable decorators for functions, methods, and classes.

Python Version License Documentation

📑 Table of Contents

🔍 Overview

This library provides a robust set of decorators for enhancing Python code functionality. It includes:

  • 🔧 Base decorator classes for extensibility
  • 📊 Monitoring and logging capabilities
  • 🔒 Validation and security features
  • 💾 Caching and performance optimization
  • 🏭 Support for both functions and classes

🚀 Installation

pip install ornator

🛠 Function & Method Decorators

Base Decorators

BeforeDecorator

Executes logic before the function call

from ornator import BeforeDecorator

class LoggingDecorator(BeforeDecorator):
    def __init__(self):
        super().__init__()
        self.pre = self.log_call

    def log_call(self, *args, **kwargs):
        print(f"[LOG] Function call with: {args}, {kwargs}")
        return args

logger = LoggingDecorator().before

@logger()
def process_data(data, pre=None):
    return f"Processing: {data}"

AfterDecorator

Modifies the function's return value

from ornator import AfterDecorator

class ResponseTransformer(AfterDecorator):
    def __init__(self):
        super().__init__()
        self.pos = self.transform_response

    def transform_response(self, result, format="json"):
        if format == "json":
            return json.dumps(result)
        return result

transformer = ResponseTransformer().after

@transformer(format="json")
def get_data():
    return {"key": "value"}

DualDecorator

Executes logic before and after the function call

from ornator import DualDecorator

class PerformanceMonitor(DualDecorator):
    def __init__(self):
        super().__init__()
        self.pre = self.start_timer
        self.pos = self.end_timer
        self.times = []

    def start_timer(self, *args, **kwargs):
        return time.time()

    def end_timer(self, result, *args, **kwargs):
        execution_time = time.time() - kwargs['pre']
        self.times.append(execution_time)
        return result

monitor = PerformanceMonitor().dual

@monitor()
def expensive_operation(pre):
    time.sleep(1)
    return "Done"

EmptyDecorator

Provides complete flexibility for custom logic

from ornator import EmptyDecorator

class Validator(EmptyDecorator):
    def validate(self, func, *args, **kwargs):
        if not args:
            raise ValueError("Arguments required")
        return func(*args, **kwargs)

validator = Validator().empty

@validator(handler=Validator().validate)
def process_data(*args):
    return sum(args)

🏗 Class Decorators

Base Class Decorators

BeforeClassDecorator

Executes logic during class instantiation

from ornator import BeforeClassDecorator

class LoggingClassDecorator(BeforeClassDecorator):
    def __init__(self):
        super().__init__()
        self.pre = self.log_instantiation
        self._log = []

    def log_instantiation(self, cls, *args, **kwargs):
        log_entry = {
            "timestamp": datetime.now(),
            "class": cls.__name__,
            "args": args
        }
        self._log.append(log_entry)
        return log_entry

logger = LoggingClassDecorator().before

@logger()
class User:
    def __init__(self, name):
        self.name = name

AfterClassDecorator

Modifies the class after its definition

from ornator import AfterClassDecorator

class ValidationDecorator(AfterClassDecorator):
    def __init__(self):
        super().__init__()
        self.pos = self.add_validation

    def add_validation(self, cls):
        original_init = cls.__init__

        def validated_init(instance, *args, **kwargs):
            for key, value in kwargs.items():
                if not isinstance(value, cls.__annotations__.get(key, object)):
                    raise TypeError(f"Invalid type for {key}")
            original_init(instance, *args, **kwargs)

        cls.__init__ = validated_init
        return cls

validator = ValidationDecorator().after

@validator()
class Person:
    name: str
    age: int

🌟 Examples

Real-World Use Cases

1. API Rate Limiting

from ornator import BeforeDecorator
import time

class RateLimiter(BeforeDecorator):
    def __init__(self, calls_per_second=1):
        super().__init__()
        self.pre = self.check_rate
        self.calls = []
        self.calls_per_second = calls_per_second

    def check_rate(self, *args, **kwargs):
        now = time.time()
        self.calls = [call for call in self.calls if now - call < 1.0]
        if len(self.calls) >= self.calls_per_second:
            raise Exception("Rate limit exceeded")
        self.calls.append(now)
        return args

limiter = RateLimiter(calls_per_second=2).before

@limiter()
def api_call(pre):
    return "API response"

2. Caching with Expiration

from ornator import DualDecorator
from datetime import datetime, timedelta

class CacheWithExpiration(DualDecorator):
    def __init__(self, expiration_minutes=60):
        super().__init__()
        self.pre = self.check_cache
        self.pos = self.update_cache
        self.cache = {}
        self.expiration = expiration_minutes

    def check_cache(self, *args, **kwargs):
        key = str(args) + str(kwargs)
        if key in self.cache:
            value, timestamp = self.cache[key]
            if datetime.now() - timestamp < timedelta(minutes=self.expiration):
                return value
        return None

    def update_cache(self, result, *args, **kwargs):
        key = str(args) + str(kwargs)
        self.cache[key] = (result, datetime.now())
        return result

cache = CacheWithExpiration(expiration_minutes=30).dual

@cache()
def expensive_computation(pre, x, y):
    if pre is not None:
        return pre
    return x + y

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

📚 Advanced Usage

Creating Custom Decorators

You can easily extend the base decorators to create your own:

class MyCustomDecorator(DualDecorator):
    def __init__(self):
        super().__init__()
        self.pre = self.my_pre_logic
        self.pos = self.my_post_logic

    def my_pre_logic(self, *args, **kwargs):
        # Your custom pre-execution logic
        return modified_args

    def my_post_logic(self, result, *args, **kwargs):
        # Your custom post-execution logic
        return modified_result

Chaining Decorators

Decorators can be chained for combined functionality:

@cache()
@validator()
@logger()
def complex_operation(*args, **kwargs):
    return result

🔧 Configuration

Each decorator can be configured through its constructor or decorator arguments:

# Configure through constructor
logger = LoggingDecorator(log_level='DEBUG').before

# Configure through decorator
@logger(format='json', timestamp=True)
def my_function():
    pass

📊 Performance Considerations

  • Use EmptyDecorator for maximum performance when custom logic is needed
  • Consider using BeforeDecorator instead of DualDecorator when post-processing isn't required
  • Cache decorator results when appropriate

🚨 Error Handling

All decorators include built-in error handling and will raise appropriate exceptions:

  • TypeError: When invalid types are provided
  • ValueError: When required values are missing
  • RuntimeError: For execution-related errors

💡 Best Practices

  1. Always extend from the appropriate base decorator
  2. Document your custom decorators
  3. Use type hints for better code clarity
  4. Follow the Single Responsibility Principle
  5. Test your decorators thoroughly

🔍 Debugging

To debug decorated functions:

import logging
logging.basicConfig(level=logging.DEBUG)

class DebugDecorator(BeforeDecorator):
    def __init__(self):
        super().__init__()
        self.pre = self.debug_call

    def debug_call(self, *args, **kwargs):
        logging.debug(f"Function call: args={args}, kwargs={kwargs}")
        return args

debug = DebugDecorator().before

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

ornator-0.0.4.tar.gz (6.4 kB view details)

Uploaded Source

Built Distribution

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

ornator-0.0.4-py3-none-any.whl (7.0 kB view details)

Uploaded Python 3

File details

Details for the file ornator-0.0.4.tar.gz.

File metadata

  • Download URL: ornator-0.0.4.tar.gz
  • Upload date:
  • Size: 6.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.9

File hashes

Hashes for ornator-0.0.4.tar.gz
Algorithm Hash digest
SHA256 c96a076f275ceb6206ee63b1a9d350b377e32e49e3bc2b9ce0e24a27fc7e5642
MD5 9e28e4a9b12b60842970bdc38b0257d3
BLAKE2b-256 6ab79e9fd00d565a715181a42130fe1ab9a66cc0d554f76079710904a0e5ee34

See more details on using hashes here.

File details

Details for the file ornator-0.0.4-py3-none-any.whl.

File metadata

  • Download URL: ornator-0.0.4-py3-none-any.whl
  • Upload date:
  • Size: 7.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.9

File hashes

Hashes for ornator-0.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 83d1abf436372a9e4587b03bbcde65809117b8051e780e367aa7ade2712854dc
MD5 2039bebbb30c575a3466ba1b6b021ea2
BLAKE2b-256 da95cfe1875ba52e76dbdba225ac960eecf117a3ba0d9dfa2ec4df9d4f6967a9

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