Skip to content

Commit

Permalink
Allows decorator list to apply to memembers prior to response generat…
Browse files Browse the repository at this point in the history
…ion. Adds decorator list testing
  • Loading branch information
incognick committed May 17, 2016
1 parent edaa910 commit 686be6e
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 4 deletions.
19 changes: 15 additions & 4 deletions flask_classful.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,21 @@ def make_proxy_method(cls, name):
i = cls()
view = getattr(i, name)

# Since the view is a bound instance method, first make it an actual function
# So function attributes work correctly
def make_func(fn):
@functools.wraps(fn)
def inner(*args, **kwargs):
return fn(*args, **kwargs)
return inner
view = make_func(view)

# Now apply the class decorator list in reverse order
# to match memeber decorator order
if cls.decorators:
for decorator in reversed(cls.decorators):
view = decorator(view)

@functools.wraps(view)
def proxy(**forgettable_view_args):
# Always use the global request object's view_args, because they
Expand Down Expand Up @@ -234,10 +249,6 @@ def proxy(**forgettable_view_args):

return response

# Decorate the proxy which is a function rather than the view which is a bound instance method
if cls.decorators:
for decorator in cls.decorators:
proxy = decorator(proxy)

return proxy

Expand Down
81 changes: 81 additions & 0 deletions test_classful/test_decorators.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
from flask import Flask, url_for
from .view_classes import DecoratedView
from .view_classes import DecoratedBoldListView
from .view_classes import DecoratedBoldItalicsListView
from .view_classes import DecoratedListMemberView
from .view_classes import DecoratedListFunctionAttributesView
from .view_classes import DecoratedListMemberFunctionAttributesView
from nose.tools import *

app = Flask("decorated")
DecoratedView.register(app)
DecoratedBoldListView.register(app)
DecoratedBoldItalicsListView.register(app)
DecoratedListMemberView.register(app)
DecoratedListFunctionAttributesView.register(app)
DecoratedListMemberFunctionAttributesView.register(app)
client = app.test_client()


Expand Down Expand Up @@ -51,9 +61,80 @@ def test_params_decorator():
resp = client.get('/decorated/params_decorator_method/')
eq_(b"Params Decorator", resp.data)


def test_params_decorator_delete():
resp = client.delete('/decorated/1234')
eq_(b"Params Decorator Delete 1234", resp.data)


def test_decorator_bold_list_get():
resp = client.get('/decorated_bold_list_view/1234')
ok_(b'<b>' in resp.data)
ok_(b'</b>' in resp.data)


def test_decorator_bold_list_index():
resp = client.get('/decorated_bold_list_view/')
ok_(b'<b>' in resp.data)
ok_(b'</b>' in resp.data)


def test_decorator_bold_italics_list_get():
resp = client.get('/decorated_bold_italics_list_view/1234')
ok_(b'<i>' in resp.data)
ok_(b'</i>' in resp.data)
ok_(b'<b>' in resp.data)
ok_(b'</b>' in resp.data)


def test_decorator_bold_italics_list_index():
resp = client.get('/decorated_bold_italics_list_view/')
ok_(b'<i>' in resp.data)
ok_(b'</i>' in resp.data)
ok_(b'<b>' in resp.data)
ok_(b'</b>' in resp.data)


def test_decorator_list_member_index():
resp = client.get('/decorated_list_member_view/')
ok_(b'<i>' in resp.data)
ok_(b'</i>' in resp.data)
ok_(b'<b>' in resp.data)
ok_(b'</b>' in resp.data)
ok_(b'<p>' not in resp.data)
ok_(b'</p>' not in resp.data)


def test_decorator_list_member_get():
resp = client.get('/decorated_list_member_view/1234')

# The order should match how functions are decorated
eq_(b'<b>', resp.data[:3])
eq_(b'<i>', resp.data[3:6])
eq_(b'<p>', resp.data[6:9])
eq_(b'</p>', resp.data[-12:-8])
eq_(b'</i>', resp.data[-8:-4])
eq_(b'</b>', resp.data[-4:])


# Verify list of decorators with attributes modify all functions in FlaskView
def test_decorator_list_function_attributes_get():
ok_(hasattr(app.view_functions['DecoratedListFunctionAttributesView:get'], '_eggs'))
eq_('scrambled', app.view_functions['DecoratedListFunctionAttributesView:get']._eggs)


# Verify list of decorators with attributes modify all functions in FlaskView
def test_decorator_list_function_attributes_index():
ok_(hasattr(app.view_functions['DecoratedListFunctionAttributesView:index'], '_eggs'))
eq_('scrambled', app.view_functions['DecoratedListFunctionAttributesView:index']._eggs)


# Verify decorator with attributes does not modify other members
def test_decorator_list_member_function_attributes_get():
eq_(hasattr(app.view_functions['DecoratedListMemberFunctionAttributesView:get'], '_eggs'), False)


# Verify decorator with attributes modify decorated memeber functions
def test_decorator_list_member_function_attributes_index():
eq_(hasattr(app.view_functions['DecoratedListMemberFunctionAttributesView:index'], '_eggs'), True)
eq_('scrambled', app.view_functions['DecoratedListMemberFunctionAttributesView:index']._eggs)
100 changes: 100 additions & 0 deletions test_classful/view_classes.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from flask import Response
from flask_classful import FlaskView, route
from functools import wraps

Expand Down Expand Up @@ -334,4 +335,103 @@ def index(self):
return "Index"


def make_bold_decorator(fn):
@wraps(fn)
def inner(*args, **kwargs):
return '<b>' + fn(*args, **kwargs) + '</b>'
return inner


def make_italics_decorator(fn):
@wraps(fn)
def inner(*args, **kwargs):
return '<i>' + fn(*args, **kwargs) + '</i>'
return inner


def make_paragraph_decorator(fn):
@wraps(fn)
def inner(*args, **kwargs):
return '<p>' + fn(*args, **kwargs) + '</p>'
return inner


class DecoratedBoldListView(FlaskView):
route_base = '/decorated_bold_list_view/'
decorators = [make_bold_decorator]

def get(self, id):
return 'Get %s'%id

def index(self):
return 'Index'


class DecoratedBoldItalicsListView(FlaskView):
route_base = '/decorated_bold_italics_list_view/'
decorators = [make_bold_decorator, make_italics_decorator]

def get(self, id):
return 'Get %s'%id

def index(self):
return 'Index'


class DecoratedListMemberView(FlaskView):
route_base = '/decorated_list_member_view/'
decorators = [
# Third Decorator
make_bold_decorator,

# Second Decorator
make_italics_decorator
]

# First decorator
@make_paragraph_decorator
def get(self, id):
return 'Get %s'%id

def index(self):
return 'Index'


def eggs_attribute_decorator(eggs_style):
def decorator(f):
# Apply the style to the function
f._eggs = eggs_style

@wraps(f)
def decorated_function(*args, **kwargs):
return f(*args, **kwargs)
return decorated_function
return decorator


class DecoratedListFunctionAttributesView(FlaskView):
route_base = '/decorated_list_function_attributes_view/'
decorators = [
make_italics_decorator,
eggs_attribute_decorator('scrambled')
]

@make_bold_decorator
def get(self, id):
return 'Get %s'%id

def index(self):
return 'Index'


class DecoratedListMemberFunctionAttributesView(FlaskView):
route_base = '/decorated_list_member_function_attributes_view/'
decorators = [make_italics_decorator]

@make_bold_decorator
def get(self, id):
return 'Get %s'%id

@eggs_attribute_decorator('scrambled')
def index(self):
return 'Index'

0 comments on commit 686be6e

Please sign in to comment.