Skip to content

Instantly share code, notes, and snippets.

@pydanny
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pydanny/9856041 to your computer and use it in GitHub Desktop.
Save pydanny/9856041 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
import functools
# Broken implementation
class ClassDecoratorOptionalArguments(object): #rename
def __init__(self, value=None):
# set the 'value' argument as an attribute
self.value = value
def __call__(self, func, value=None):
@functools.wraps(func)
def wrapped(*args, **kwargs):
print(self.value) # the 'value' variable is in self
# Before function call logic goes here
result = func(*args, **kwargs)
# After function call logic goes here
return result
return wrapped
# Failing test
def test_ClassDecoratorOptionalArguments(capsys):
@ClassDecoratorOptionalArguments(value="PEP 0318")
def return_6():
return 6
# does function return correct value?
assert return_6() == 6
# Did the value get printed?
out, err = capsys.readouterr()
assert out == "PEP 0318\n"
# Try it again but without the decorator argument.
@ClassDecoratorOptionalArguments
def return_7():
return 7
# does function return correct value?
assert return_7() == 7
# Did the value get printed?
out, err = capsys.readouterr()
assert out == "None\n"
@GrahamDumpleton
Copy link

You really don't want to do this combination. It is a bit silly.

And one case you loose introspection ability.

In short, don't suggest this as an alternative as is not useful.

# -*- coding: utf-8 -*-
import functools


# Broken implementation
class ClassDecoratorOptionalArguments(object): #rename

    def __init__(self, func=None, value=None):
        # set the 'value' argument as an attribute
        self.value = value

        self.func = func

    def wrapper(self, func):
        @functools.wraps(func)
        def wrapped(*args, **kwargs):
            print(self.value) # the 'value' variable is in self
            # Before function call logic goes here
            result = func(*args, **kwargs)
            # After function call logic goes here
            return result
        return wrapped

    def execute(self, *args, **kwargs):
        print(self.value) # the 'value' variable is in self
        # Before function call logic goes here
        result = self.func(*args, **kwargs)
        # After function call logic goes here
        return result

    def __call__(self, *args, **kwargs):
        if self.func is None:
            return self.wrapper(*args, **kwargs)
        else:
            return self.execute(*args, **kwargs)

# Failing test
def test_ClassDecoratorOptionalArguments(capsys):

    @ClassDecoratorOptionalArguments(value="PEP 0318")
    def return_6():
        return 6

    # does function return correct value?
    assert return_6() == 6

    # Did the value get printed?
    out, err = capsys.readouterr()
    assert out == "PEP 0318\n"

    # Try it again but without the decorator argument.
    @ClassDecoratorOptionalArguments
    def return_7():
        return 7

    # does function return correct value?
    assert return_7() == 7

    # Did the value get printed?
    out, err = capsys.readouterr()
    assert out == "None\n"

@GrahamDumpleton
Copy link

So for class decorator with optional arguments you should use your normal class decorator in combination with a function.

# -*- coding: utf-8 -*-
import functools

class ClassDecorator(object): # rename as needed
    def __init__(self, func, value=None):
        self.wrapped = func
        functools.update_wrapper(self, func)
        self.value = value

    def __call__(self, *args, **kwargs):
        print(self.value) # the 'value' variable is in self
        # Before function call logic goes here
        result = self.wrapped(*args, **kwargs)
        # After function call logic goes here
        return result

def ClassDecoratorOptionalArguments(func=None, value=None):
    if func is not None:
        return ClassDecorator(func, value)
    else:
        return functools.partial(ClassDecoratorOptionalArguments, value=value)

# Failing test
def test_ClassDecoratorOptionalArguments(capsys):

    @ClassDecoratorOptionalArguments(value="PEP 0318")
    def return_6():
        return 6

    # does function return correct value?
    assert return_6() == 6

    # Did the value get printed?
    out, err = capsys.readouterr()
    assert out == "PEP 0318\n"

    # Try it again but without the decorator argument.
    @ClassDecoratorOptionalArguments
    def return_7():
        return 7

    # does function return correct value?
    assert return_7() == 7

    # Did the value get printed?
    out, err = capsys.readouterr()
    assert out == "None\n"

@GrahamDumpleton
Copy link

I am not sure you decorator function with optional arguments is correct in the post. Should be something like:

# -*- coding: utf-8 -*-
import functools

def decorator_optional_args_function(func=None, value=None): #rename
    def _wrapped(func):
        @functools.wraps(func)
        def _returned_wrapper(*args, **kwargs):
            print(value) # the 'value' variable is in scope
            # Before function call logic goes here
            result = func(*args, **kwargs)
            # After function call logic goes here
            return result
        return _returned_wrapper

    if func is not None:
        return _wrapped(func)
    else:
        return functools.partial(decorator_optional_args_function, value=value)

def test_ClassDecoratorOptionalArguments(capsys):

    @decorator_optional_args_function(value="PEP 0318")
    def return_6():
        return 6

    # does function return correct value?
    assert return_6() == 6

    # Did the value get printed?
    out, err = capsys.readouterr()
    assert out == "PEP 0318\n"

    # Try it again but without the decorator argument.
    @decorator_optional_args_function
    def return_7():
        return 7

    # does function return correct value?
    assert return_7() == 7

    # Did the value get printed?
    out, err = capsys.readouterr()
    assert out == "None\n"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment