Skip to main content

A Python library for simplifying HTTP integrations with REST APIs, featuring decorators for authentication handling and request management.

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 advanced-decorators

🛠 Function & Method Decorators

Base Decorators

BeforeDecorator

Executes logic before the function call

from decorators 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(pre, data):
    return f"Processing: {data}"

AfterDecorator

Modifies the function's return value

from decorators 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 decorators 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 decorators 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 decorators 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 decorators 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 decorators 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 decorators 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

For more information and updates, please visit our GitHub repository.

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.1.tar.gz (6.9 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.1-py3-none-any.whl (6.1 kB view details)

Uploaded Python 3

File details

Details for the file Ornator-0.0.1.tar.gz.

File metadata

  • Download URL: Ornator-0.0.1.tar.gz
  • Upload date:
  • Size: 6.9 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.1.tar.gz
Algorithm Hash digest
SHA256 ab9be437391b196c7c177d50afb1602549127b2503aa3c9a28f8159417224863
MD5 4bfd59d7be5b945883051139f551e712
BLAKE2b-256 a10d9ed991ac207574d65a938601a7e0c8189487af26cfbf772b30f317838451

See more details on using hashes here.

File details

Details for the file Ornator-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: Ornator-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 6.1 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a7f532593f5e9ebc1c97d0ea04e6976e84ce42a234ef0c0ef19061d1cd4df01b
MD5 715224a99afc275d37d589c25342db43
BLAKE2b-256 0403b5f062b37b895bc915af171749da5f37fc9c37df5405ec579df668a25be0

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