Navigation Menu

Skip to content
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

Make Query instanciation lazy #35

Merged
merged 1 commit into from Nov 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/changelog.md
Expand Up @@ -8,6 +8,15 @@ A changelog:

*Analogy: git blame :p*

## In progress

* **Breaking changes**:
* `Request` and `Response` classes now take `app` as init parameter. It
allows lazy parsing of the query while keeping the `Query` class reference
on `Roll` application.
([#35](https://github.com/pyrates/roll/pull/35))


## 0.7.0 - 2017-11-27

* **Breaking changes**:
Expand Down
28 changes: 18 additions & 10 deletions roll/__init__.py
Expand Up @@ -109,26 +109,36 @@ class Request(dict):

The default parsing is made by `httptools.HttpRequestParser`.
"""
__slots__ = ('url', 'path', 'query_string', 'query', 'method', 'body',
'headers', 'route', '_cookies')
__slots__ = ('app', 'url', 'path', 'query_string', '_query', 'method',
'body', 'headers', 'route', '_cookies')

def __init__(self):
def __init__(self, app):
self.app = app
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows to keep Query reference on Roll.
Other option is to have Query on Request itself, but that means overriding Request AND Query when we'd only need to override Query.

self.headers = {}
self.body = b''
self._cookies = None
self._query = None

@property
def cookies(self):
if self._cookies is None:
self._cookies = parse(self.headers.get('COOKIE', ''))
return self._cookies

@property
def query(self):
if self._query is None:
parsed_qs = parse_qs(self.query_string, keep_blank_values=True)
self._query = self.app.Query(parsed_qs)
return self._query


class Response:
"""A container for `status`, `headers` and `body`."""
__slots__ = ('_status', 'headers', 'body', '_cookies')
__slots__ = ('app', '_status', 'headers', 'body', '_cookies')

def __init__(self):
def __init__(self, app):
self.app = app
self._status = None
self.body = b''
self.status = HTTPStatus.OK
Expand Down Expand Up @@ -178,7 +188,7 @@ def data_received(self, data: bytes):
except HttpParserError:
# If the parsing failed before on_message_begin, we don't have a
# response.
self.response = self.app.Response()
self.response = self.app.Response(self.app)
self.response.status = HTTPStatus.BAD_REQUEST
self.response.body = b'Unparsable request'
self.write()
Expand All @@ -199,12 +209,10 @@ def on_url(self, url: bytes):
parsed = parse_url(url)
self.request.path = unquote(parsed.path.decode())
self.request.query_string = (parsed.query or b'').decode()
parsed_qs = parse_qs(self.request.query_string, keep_blank_values=True)
self.request.query = self.app.Query(parsed_qs)

def on_message_begin(self):
self.request = self.app.Request()
self.response = self.app.Response()
self.request = self.app.Request(self.app)
self.response = self.app.Response(self.app)

def on_message_complete(self):
self.request.method = self.parser.get_method().decode().upper()
Expand Down
2 changes: 1 addition & 1 deletion tests/test_request.py
Expand Up @@ -324,7 +324,7 @@ async def test_request_get_unknown_cookie_key_raises_keyerror(protocol):


async def test_can_store_arbitrary_keys_on_request():
request = Request()
request = Request(None)
request['custom'] = 'value'
assert 'custom' in request
assert request['custom'] == 'value'