Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions flask_negotiation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

Provides better content-negotiation for flask.
"""
from __future__ import absolute_import

from flask import Response, request, abort

from renderers import TemplateRenderer
from decorators import provides
from media_type import acceptable_media_types, best_renderer, MediaType
from .renderers import TemplateRenderer
from .decorators import provides
from .media_type import acceptable_media_types, best_renderer, MediaType

__all__ = ('Render', 'MediaType', 'provides')

Expand Down Expand Up @@ -74,5 +76,5 @@ def content_view():
if renderer is None:
abort(406)
body = renderer.render(data, template, ctx)
return Response(body, status, headers, unicode(rendered_media_type),
content_type=unicode(rendered_media_type))
return Response(body, status, headers, str(rendered_media_type),
content_type=str(rendered_media_type))
8 changes: 5 additions & 3 deletions flask_negotiation/decorators.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
""":mod:`decorators` --- Decorators for Flask views
===================================================
"""
from __future__ import absolute_import

from functools import wraps

from flask import request
from werkzeug.exceptions import NotAcceptable

from renderers import Renderer
from media_type import acceptable_media_types, MediaType, choose_media_type
from .renderers import Renderer
from .media_type import acceptable_media_types, MediaType, choose_media_type


def provides(media_type, *args, **kwargs):
Expand Down Expand Up @@ -58,7 +60,7 @@ def handle_type(provide_type):
if isinstance(media_type, MediaType):
media_types.append(media_type)
elif isinstance(media_type, type) and issubclass(media_type, Renderer):
media_types += map(MediaType, media_type.__media_types__)
media_types += list(map(MediaType, media_type.__media_types__))
elif isinstance(media_type, Renderer):
media_types += media_type.media_types
else:
Expand Down
32 changes: 19 additions & 13 deletions flask_negotiation/media_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
HTTP media type
"""

from functools import cmp_to_key

def parse_header(s):
"""Parses parameter header
Expand Down Expand Up @@ -47,7 +48,7 @@ def __init__(self, raw):
self.main_type, sep, self.sub_type = self.media_type.partition('/')

def __contains__(self, other):
for k, v in self.params.iteritems():
for k, v in self.params.items():
if k != 'q' and other.params.get(k, None) != v:
return False
if self.main_type == '*' and self.sub_type == '*':
Expand All @@ -59,25 +60,24 @@ def __contains__(self, other):
return self == other

def __eq__(self, other):
if isinstance(other, basestring):
return unicode(self) == other
if isinstance(other, str):
return str(self) == other
return (self.main_type == other.main_type and
self.sub_type == other.sub_type)

def __repr__(self):
return '<media type:' + str(self) + '>'

def __str__(self):
return unicode(self).encode('utf-8')
return '; '.join(['%s/%s' % (self.main_type, self.sub_type)] +
['%s=%s' % (k, v)
for k, v in self.params.items()])

def __unicode__(self):
return u'; '.join([u'%s/%s' % (self.main_type, self.sub_type)] +
[u'%s=%s' % (k, v)
for k, v in self.params.iteritems()])
def __lt__(self, other):
return float(self.quality) < float(other.quality)

def __cmp__(self, other):
return cmp(float(self.quality),
float(other.quality))
def __gt__(self, other):
return float(self.quality) > float(other.quality)

@property
def quality(self):
Expand Down Expand Up @@ -110,6 +110,9 @@ def best_renderer(renderers, media_types):
if not choosen_items:
return None, None

def cmp(first, second):
return first - second

def cmp_types(first, second):
renderer1, choosen1, media_type1 = first
renderer2, choosen2, media_type2 = second
Expand All @@ -120,7 +123,7 @@ def cmp_types(first, second):
media_types.index(media_type1))
return cmp(media_type1.quality, media_type2.quality)

return tuple(sorted(choosen_items, cmp=cmp_types)[-1][:2])
return tuple(sorted(choosen_items, key=cmp_to_key(cmp_types))[-1][:2])


def choose_media_type(acceptables, media_types):
Expand All @@ -138,6 +141,9 @@ def choose_media_type(acceptables, media_types):
if not choosen:
return None

def cmp(first, second):
return first - second

def cmp_types(first, second):
acceptable1, media_type1 = first
acceptable2, media_type2 = second
Expand All @@ -146,7 +152,7 @@ def cmp_types(first, second):
acceptables.index(acceptable1))
return cmp(acceptable.quality, acceptable.quality)

return sorted(choosen, cmp=cmp_types)[-1][0]
return sorted(choosen, key=cmp_to_key(cmp_types))[-1][0]


def can_accept(acceptables, media_types):
Expand Down
9 changes: 6 additions & 3 deletions flask_negotiation/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@

Renderers
"""
from __future__ import absolute_import

import json
import six
from abc import ABCMeta, abstractmethod
from flask import render_template
from functools import wraps
from media_type import MediaType
from .media_type import MediaType


@six.add_metaclass(ABCMeta)
class Renderer(object):
"""Base renderer class.
"""
__metaclass__ = ABCMeta

__media_types__ = ()
"""A collection of supporting media-type :class:`string`s, subclasses must
Expand Down Expand Up @@ -90,7 +93,7 @@ class FunctionRenderer(Renderer):
def __init__(self, fn, media_types):
super(FunctionRenderer, self).__init__()
self.fn = fn
self.__media_types__ = map(unicode, media_types)
self.__media_types__ = list(map(str, media_types))

def render(self, data, template=None, ctx=None):
return self.fn(data, template=template, ctx=ctx)
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@

requires = [
'Flask',
'six'
]

setup(name='Flask-Negotiation',
version='0.1.9',
version='0.1.10',
url='http://blog.hardtack.me/',
author='GunWoo Choi',
author_email='6566gun@gmail.com',
Expand Down
38 changes: 19 additions & 19 deletions tests/test_negotiation.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ def second():
}
rv = client.get('/render', headers=headers)
assert 200 == rv.status_code
assert '<html><body>value</body></html>' == rv.data
assert b'<html><body>value</body></html>' == rv.data

headers = {
'Accept': 'application/json; q=0.7, text/html; q=0.8'
}
rv = client.get('/render', headers=headers)
assert 200 == rv.status_code
assert '<html><body>value</body></html>' == rv.data
assert b'<html><body>value</body></html>' == rv.data

headers = {
'Accept': 'application/json; q=0.7, text/html; q=0.8'
Expand Down Expand Up @@ -187,12 +187,12 @@ def fifth(provide_type):
headers = {
'Accept': 'application/json'
}
assert 'application/json' == client.get('/5', headers=headers).data
assert b'application/json' == client.get('/5', headers=headers).data

headers = {
'Accept': 'text/html'
}
assert 'text/html' == client.get('/5', headers=headers).data
assert b'text/html' == client.get('/5', headers=headers).data


def test_media_type():
Expand Down Expand Up @@ -229,39 +229,39 @@ def test_acceptablility():
assert can_accept(acceptables, media_types)

# Partitial wildcard
media_types = map(MediaType, ['text/*'])
media_types = list(map(MediaType, ['text/*']))
acceptables = map(MediaType, ['application/json'])
assert not can_accept(acceptables, media_types)

acceptables = map(MediaType, ['text/html'])
assert can_accept(acceptables, media_types)

# Multiple acceptables
media_types = map(MediaType, ['text/html'])
acceptables = map(MediaType, ['application/json', 'text/html'])
media_types = list(map(MediaType, ['text/html']))
acceptables = list(map(MediaType, ['application/json', 'text/html']))
assert can_accept(acceptables, media_types)

media_types = map(MediaType, ['image/jpeg'])
acceptables = map(MediaType, ['application/json', 'text/html'])
media_types = list(map(MediaType, ['image/jpeg']))
acceptables = list(map(MediaType, ['application/json', 'text/html']))
assert not can_accept(acceptables, media_types)

# Multiple media types
media_types = map(MediaType, ['text/*', 'application/json'])
acceptables = map(MediaType, ['application/json'])
media_types = list(map(MediaType, ['text/*', 'application/json']))
acceptables = list(map(MediaType, ['application/json']))
assert can_accept(acceptables, media_types)
acceptables = map(MediaType, ['text/html'])
acceptables = list(map(MediaType, ['text/html']))
assert can_accept(acceptables, media_types)

acceptables = map(MediaType, ['image/jpeg'])
acceptables = list(map(MediaType, ['image/jpeg']))
assert not can_accept(acceptables, media_types)

# Multiple both
media_types = map(MediaType, ['text/html', 'application/*'])
acceptables = map(MediaType, ['application/json', 'image/jpeg'])
media_types = list(map(MediaType, ['text/html', 'application/*']))
acceptables = list(map(MediaType, ['application/json', 'image/jpeg']))
assert can_accept(acceptables, media_types)

media_types = map(MediaType, ['text/html', 'application/*'])
acceptables = map(MediaType, ['image/png', 'image/jpeg'])
media_types = list(map(MediaType, ['text/html', 'application/*']))
acceptables = list(map(MediaType, ['image/png', 'image/jpeg']))
assert not can_accept(acceptables, media_types)


Expand All @@ -278,10 +278,10 @@ def test_choosing():

assert png_type == choose_media_type(
[png_type, jpeg_type],
map(MediaType, ['text/html', 'application/*', 'image/*'])
list(map(MediaType, ['text/html', 'application/*', 'image/*']))
)

assert html_type == choose_media_type(
[json_type, html_type],
map(MediaType, ['text/html', 'application/*'])
list(map(MediaType, ['text/html', 'application/*']))
)