New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use bytes or unicode to get args #164
Changes from 5 commits
7d43d39
5d929d5
f3c3420
526a37e
8c28fee
44aac2a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ | |
from twisted.internet import defer | ||
from twisted.python import log, failure | ||
from twisted.python.compat import unicode, intToBytes | ||
from twisted.web import server | ||
from twisted.web import http, server | ||
from twisted.web.iweb import IRenderable | ||
from twisted.web.resource import Resource, IResource, getChildForRequest | ||
from twisted.web.server import NOT_DONE_YET | ||
|
@@ -280,3 +280,39 @@ def write_response(r): | |
d.addCallback(write_response).addErrback(log.err, _why="Unhandled Error writing response") | ||
|
||
return server.NOT_DONE_YET | ||
|
||
|
||
|
||
class KleinHTTPRequest(server.Request): | ||
|
||
def getArg(self, key): | ||
""" | ||
Get a single arg value. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You forgot to document |
||
|
||
@raises KeyError: If key doesn't exist | ||
@raises ValueError: If there is more than 1 value | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You forgot about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch |
||
@return: L{list} of L{bytes} | ||
""" | ||
key = ensure_utf8_bytes(key) | ||
value = self.args[key] | ||
if len(value) != 1: | ||
raise ValueError('Too many values for: {0}'.format(key)) | ||
return value[0] | ||
|
||
def getArgs(self, key): | ||
""" | ||
Get the list of values for a key. | ||
|
||
@return: L{list} of L{bytes} | ||
""" | ||
key = ensure_utf8_bytes(key) | ||
return self.args.get(key, []) | ||
|
||
|
||
|
||
class KleinSite(server.Site): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this intended to be public? Do we need to use it for all Klein applications now? That seems like a pretty big change to quietly be foisting on all Klein applications. (That implies If it is intended to be public, some docstrings seems like a bare minimum. Also, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a rather ugly hack on my part and I'd like to move away from it. At the time I did not know of alternative ways to overload |
||
def buildProtocol(self, addr): | ||
channel = http.HTTPFactory.buildProtocol(self, addr) | ||
channel.requestFactory = KleinHTTPRequest | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you considered using the |
||
channel.site = self | ||
return channel |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
from __future__ import absolute_import | ||
|
||
from klein import Klein | ||
from klein.resource import KleinHTTPRequest, KleinSite | ||
from klein.test.util import TestCase | ||
from twisted.web.test.requesthelper import DummyChannel | ||
|
||
class BytesUnicodeTest(TestCase): | ||
|
||
def setUp(self): | ||
self.encoding = 'utf-8' | ||
app = Klein() | ||
site = KleinSite(app.resource()) | ||
channel = site.buildProtocol('127.0.0.1') | ||
self.request = channel.requestFactory(DummyChannel(), None) | ||
self.request.args = {} | ||
|
||
def test_getArg(self): | ||
str_key = 'test' | ||
bytes_key = str_key.encode(self.encoding) | ||
value = b'hello world' | ||
|
||
self.request.args[bytes_key] = [value] | ||
self.assertEquals(self.request.getArg(str_key), value) | ||
self.assertEquals(self.request.getArg(bytes_key), value) | ||
|
||
def test_getArg_not_1(self): | ||
""" | ||
Raise exception if there are more or less values than 1 | ||
""" | ||
str_key = 'test' | ||
bytes_key = str_key.encode(self.encoding) | ||
values = [] | ||
|
||
self.request.args[bytes_key] = values | ||
self.assertRaises(ValueError, self.request.getArg, str_key) | ||
self.assertRaises(ValueError, self.request.getArg, bytes_key) | ||
|
||
values.extend([b'hello', b'world']) | ||
self.assertRaises(ValueError, self.request.getArg, str_key) | ||
self.assertRaises(ValueError, self.request.getArg, bytes_key) | ||
|
||
def test_getArgs(self): | ||
str_key = 'test' | ||
bytes_key = str_key.encode(self.encoding) | ||
values = [b'hello world', b'hey earth'] | ||
self.request.args[bytes_key] = values | ||
|
||
self.assertEquals(len(self.request.getArgs(str_key)), len(values)) | ||
self.assertEquals(self.request.getArgs(str_key), values) | ||
self.assertEquals(self.request.getArgs(bytes_key), values) | ||
|
||
def test_getArgs_no_key(self): | ||
""" | ||
By default, an empty list is returned if a key doesn't exist | ||
""" | ||
str_key = 'test' | ||
bytes_key = str_key.encode(self.encoding) | ||
|
||
self.assertEquals(self.request.getArgs(str_key), []) | ||
self.assertEquals(self.request.getArgs(bytes_key), []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't like the name
getArg
. "args" are an incredibly ill-defined mess in Twisted, mixing together query parameters ("GET args") and URL-encoded or multipart form-values in the body ("POST args" or "form args"). If we're going to try to do something better, it would be nice to clean this up a bit. Even if it is accessing the underlying.args
, I feel like we should be disambiguating these namespaces.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a big fan of getters and setters either but it was the simple and easy to understand. I had a more elaborate solution using a dict-like object for the
args
variable but other devs weren't convinced with it and ultimatly it was scraped in favor ofgetArgs
. As far as the Twisted mess, you provided a ticket #228 but work on this might have stalled. Any hope of reviving that?