From 21d736ddbe535dbb5e733da61dba55f1c252f98d Mon Sep 17 00:00:00 2001 From: kgriffs Date: Wed, 11 Sep 2013 15:39:53 -0500 Subject: [PATCH] fix: Refine client_accepts --- falcon/request.py | 30 +++++++++--------------------- falcon/tests/test_req_vars.py | 32 +++++++++++--------------------- falcon/util.py | 4 +++- 3 files changed, 23 insertions(+), 43 deletions(-) diff --git a/falcon/request.py b/falcon/request.py index 9e8f893..214f2f6 100644 --- a/falcon/request.py +++ b/falcon/request.py @@ -31,8 +31,6 @@ DEFAULT_ERROR_LOG_FORMAT = (u'{0:%Y-%m-%d %H:%M:%S} [FALCON] [ERROR]' TRUE_STRINGS = ('true', 'True', 'yes') FALSE_STRINGS = ('false', 'False', 'no') -MEDIA_TYPES_XML = ('application/xml', 'text/xml') - class InvalidHeaderValueError(HTTPBadRequest): def __init__(self, msg, href=None, href_text=None): @@ -152,14 +150,13 @@ class Request(object): @property def client_accepts_xml(self): """Return True if the Accept header indicates XML support.""" - return self.client_accepts(MEDIA_TYPES_XML) + return self.client_accepts('application/xml') - def client_accepts(self, media_types): + def client_accepts(self, media_type): """Returns the client's preferred media type. Args: - media_types: One or more media types. May be a single string ( - of type str), or an iterable collection of strings. + media_type: Media type to check Returns: True IFF the client has indicated in the Accept header that @@ -170,23 +167,14 @@ class Request(object): # PERF(kgriffs): Usually the following will be true, so # try it first. - if isinstance(media_types, str): - if (accept == media_types) or (accept == '*/*'): - return accept - - # NOTE(kgriffs): Convert to a collection to be compatible - # with mimeparse.best_matchapplication/xhtml+xml - media_types = (media_types,) - - # NOTE(kgriffs): Heuristic to quickly check another common case. If - # accept is a single type, and it is found in media_types verbatim, - # return the media type immediately. - elif accept in media_types: - return accept + if (accept == media_type) or (accept == '*/*'): + return True # Fall back to full-blown parsing - preferred_type = self.client_prefers(media_types) - return preferred_type is not None + try: + return mimeparse.quality(media_type, accept) != 0.0 + except ValueError: + return False def client_prefers(self, media_types): """Returns the client's preferred media type given several choices. diff --git a/falcon/tests/test_req_vars.py b/falcon/tests/test_req_vars.py index c36d735..c292420 100644 --- a/falcon/tests/test_req_vars.py +++ b/falcon/tests/test_req_vars.py @@ -87,31 +87,27 @@ class TestReqVars(testing.TestBase): headers = {'Accept': 'application/xml'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) - self.assertTrue(req.client_accepts(['application/xml'])) headers = {'Accept': '*/*'} req = Request(testing.create_environ(headers=headers)) - self.assertTrue(req.client_accepts(['application/xml'])) + self.assertTrue(req.client_accepts('application/xml')) headers = {} # NOTE(kgriffs): Equivalent to '*/*' per RFC req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts('application/xml')) - self.assertTrue(req.client_accepts(['application/xml'])) headers = {'Accept': 'application/json'} req = Request(testing.create_environ(headers=headers)) - self.assertFalse(req.client_accepts(['application/xml'])) + self.assertFalse(req.client_accepts('application/xml')) headers = {'Accept': 'application/xm'} req = Request(testing.create_environ(headers=headers)) - self.assertFalse(req.client_accepts(['application/xml'])) + self.assertFalse(req.client_accepts('application/xml')) headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) - self.assertTrue(req.client_accepts(['application/json'])) - self.assertTrue(req.client_accepts(['application/xml'])) - self.assertTrue(req.client_accepts(['application/json', - 'application/xml'])) + self.assertTrue(req.client_accepts('application/json')) + self.assertTrue(req.client_accepts('application/xml')) headers = {'Accept': 'text/*'} req = Request(testing.create_environ(headers=headers)) @@ -124,9 +120,6 @@ class TestReqVars(testing.TestBase): self.assertTrue(req.client_accepts('text/plain')) self.assertTrue(req.client_accepts('text/csv')) self.assertTrue(req.client_accepts('application/xhtml+xml')) - self.assertTrue(req.client_accepts(('application/xhtml+xml', - 'text/plain', - 'text/csv'))) headers = {'Accept': 'text/*; q=0.1, application/xhtml+xml; q=0.5'} req = Request(testing.create_environ(headers=headers)) @@ -148,15 +141,7 @@ class TestReqVars(testing.TestBase): self.assertTrue(req.client_accepts_xml) self.assertFalse(req.client_accepts_json) - headers = {'Accept': 'text/xml'} - req = Request(testing.create_environ(headers=headers)) - self.assertTrue(req.client_accepts_xml) - - headers = {'Accept': 'text/*'} - req = Request(testing.create_environ(headers=headers)) - self.assertTrue(req.client_accepts_xml) - - headers = {'Accept': 'text/xml, application/xml'} + headers = {'Accept': 'application/*'} req = Request(testing.create_environ(headers=headers)) self.assertTrue(req.client_accepts_xml) @@ -188,6 +173,11 @@ class TestReqVars(testing.TestBase): preferred_type = req.client_prefers(['application/xhtml+xml']) self.assertEquals(preferred_type, 'application/xhtml+xml') + headers = {'Accept': '3p12845j;;;asfd;'} + req = Request(testing.create_environ(headers=headers)) + preferred_type = req.client_prefers(['application/xhtml+xml']) + self.assertEquals(preferred_type, None) + def test_range(self): headers = {'Range': '10-'} req = Request(testing.create_environ(headers=headers)) diff --git a/falcon/util.py b/falcon/util.py index c1c73a8..3dd61b4 100644 --- a/falcon/util.py +++ b/falcon/util.py @@ -83,7 +83,9 @@ def to_query_str(params): elif v is False: v = 'false' elif isinstance(v, list): - v = ','.join([str(i) for i in v]) + # PERF(kgriffs): map is faster than list comprehension in + # py26 and py33. No significant different in py27 + v = ','.join(map(str, v)) else: v = str(v)