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

Resp prepare #525

Merged
merged 8 commits into from
Sep 25, 2015
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ CHANGES
=======

0.18.0a0 (XX-XX-XXXX)
-------------------
---------------------

- Use errors.HttpProcessingError.message as HTTP error reason and
message #459
Expand Down
2 changes: 1 addition & 1 deletion aiohttp/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def handle_request(self, message, payload):
except HTTPException as exc:
resp = exc

resp_msg = resp.start(request)
resp_msg = yield from resp.prepare(request)
yield from resp.write_eof()

# notify server about keep-alive
Expand Down
25 changes: 21 additions & 4 deletions aiohttp/web_reqrep.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,9 +425,14 @@ def _copy_cookies(self):
self.headers.add(hdrs.SET_COOKIE, value)

@property
def started(self):
def prepared(self):
return self._resp_impl is not None

@property
def started(self):
warnings.warn('use Response.prepared instead', DeprecationWarning)
return self.prepared

@property
def status(self):
return self._status
Expand Down Expand Up @@ -612,27 +617,39 @@ def _start_pre_check(self, request):
return None

def _start_compression(self, request):
def start(coding):
def _start(coding):
if coding != ContentCoding.identity:
self.headers[hdrs.CONTENT_ENCODING] = coding.value
self._resp_impl.add_compression_filter(coding.value)
self.content_length = None

if self._compression_force:
start(self._compression_force)
_start(self._compression_force)
else:
accept_encoding = request.headers.get(
hdrs.ACCEPT_ENCODING, '').lower()
for coding in ContentCoding:
if coding.value in accept_encoding:
start(coding)
_start(coding)
return

def start(self, request):
warnings.warn('use .prepare(request) instead', DeprecationWarning)
Copy link
Member

Choose a reason for hiding this comment

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

Seems .start remain plain old function while .prepare is coroutine. Are they really interchangeable during deprecation period?

Copy link
Member Author

Choose a reason for hiding this comment

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

They are do interchangeable with a note: .prepare() will process signal handing for response sending but .start() will not.

Copy link
Member

Choose a reason for hiding this comment

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

Good to know.

resp_impl = self._start_pre_check(request)
if resp_impl is not None:
return resp_impl

return self._start(request)

@asyncio.coroutine
def prepare(self, request):
resp_impl = self._start_pre_check(request)
if resp_impl is not None:
return resp_impl

return self._start(request)

def _start(self, request):
self._req = request
keep_alive = self._keep_alive
if keep_alive is None:
Expand Down
2 changes: 1 addition & 1 deletion aiohttp/web_urldispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ def handle(self, request):
file_size = st.st_size

resp.content_length = file_size
resp.start(request)
yield from resp.prepare(request)

with open(filepath, 'rb') as f:
yield from self._sendfile(request, resp, f, file_size)
Expand Down
43 changes: 32 additions & 11 deletions aiohttp/web_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,19 @@ def __init__(self, *,
self._autoclose = autoclose
self._autoping = autoping

def start(self, request):
@asyncio.coroutine
def prepare(self, request):
Copy link
Member

Choose a reason for hiding this comment

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

Coroutine decorator missed here

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

# make pre-check to don't hide it by do_handshake() exceptions
resp_impl = self._start_pre_check(request)
if resp_impl is not None:
return resp_impl

parser, protocol, writer = self._pre_start(request)
resp_impl = yield from super().prepare(request)
self._post_start(request, parser, protocol, writer)
return resp_impl

def _pre_start(self, request):
try:
status, headers, parser, writer, protocol = do_handshake(
request.method, request.headers, request.transport,
Expand All @@ -59,17 +66,27 @@ def start(self, request):
for k, v in headers:
self.headers[k] = v
self.force_close()
return parser, protocol, writer

resp_impl = super().start(request)

def _post_start(self, request, parser, protocol, writer):
self._reader = request._reader.set_parser(parser)
self._writer = writer
self._protocol = protocol
self._loop = request.app.loop

def start(self, request):
warnings.warn('use .prepare(request) instead', DeprecationWarning)
# make pre-check to don't hide it by do_handshake() exceptions
resp_impl = self._start_pre_check(request)
if resp_impl is not None:
return resp_impl

parser, protocol, writer = self._pre_start(request)
resp_impl = super().start(request)
self._post_start(request, parser, protocol, writer)
return resp_impl

def can_start(self, request):
def can_prepare(self, request):
if self._writer is not None:
raise RuntimeError('Already started')
try:
Expand All @@ -81,6 +98,10 @@ def can_start(self, request):
else:
return True, protocol

def can_start(self, request):
warnings.warn('use .can_prepare(request) instead', DeprecationWarning)
return self.can_prepare(request)

@property
def closed(self):
return self._closed
Expand All @@ -98,22 +119,22 @@ def exception(self):

def ping(self, message='b'):
if self._writer is None:
raise RuntimeError('Call .start() first')
raise RuntimeError('Call .prepare() first')
if self._closed:
raise RuntimeError('websocket connection is closing')
self._writer.ping(message)

def pong(self, message='b'):
# unsolicited pong
if self._writer is None:
raise RuntimeError('Call .start() first')
raise RuntimeError('Call .prepare() first')
if self._closed:
raise RuntimeError('websocket connection is closing')
self._writer.pong(message)

def send_str(self, data):
if self._writer is None:
raise RuntimeError('Call .start() first')
raise RuntimeError('Call .prepare() first')
if self._closed:
raise RuntimeError('websocket connection is closing')
if not isinstance(data, str):
Expand All @@ -122,7 +143,7 @@ def send_str(self, data):

def send_bytes(self, data):
if self._writer is None:
raise RuntimeError('Call .start() first')
raise RuntimeError('Call .prepare() first')
if self._closed:
raise RuntimeError('websocket connection is closing')
if not isinstance(data, (bytes, bytearray, memoryview)):
Expand Down Expand Up @@ -151,7 +172,7 @@ def write_eof(self):
@asyncio.coroutine
def close(self, *, code=1000, message=b''):
if self._writer is None:
raise RuntimeError('Call .start() first')
raise RuntimeError('Call .prepare() first')

if not self._closed:
self._closed = True
Expand Down Expand Up @@ -190,7 +211,7 @@ def close(self, *, code=1000, message=b''):
@asyncio.coroutine
def receive(self):
if self._reader is None:
raise RuntimeError('Call .start() first')
raise RuntimeError('Call .prepare() first')
if self._waiting:
raise RuntimeError('Concurrent call to receive() is not allowed')

Expand Down Expand Up @@ -239,7 +260,7 @@ def receive(self):
self._waiting = False

@asyncio.coroutine
def receive_msg(self): # pragma: no cover
def receive_msg(self):
warnings.warn(
'receive_msg() coroutine is deprecated. use receive() instead',
DeprecationWarning)
Expand Down
2 changes: 1 addition & 1 deletion docs/web.rst
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ using response's methods:
def websocket_handler(request):

ws = web.WebSocketResponse()
ws.start(request)
yield from ws.prepare(request)

while True:
msg = yield from ws.receive()
Expand Down
66 changes: 55 additions & 11 deletions docs/web_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -339,11 +339,10 @@ StreamResponse
The most important thing you should know about *response* --- it
is *Finite State Machine*.

That means you can do any manipulations with *headers*,
*cookies* and *status code* only before :meth:`start`
called.
That means you can do any manipulations with *headers*, *cookies*
and *status code* only before :meth:`prepare` coroutine is called.

Once you call :meth:`start` any change of
Once you call :meth:`prepare` any change of
the *HTTP header* part will raise :exc:`RuntimeError` exception.

Any :meth:`write` call after :meth:`write_eof` is also forbidden.
Expand All @@ -355,11 +354,19 @@ StreamResponse
parameter. Otherwise pass :class:`str` with
arbitrary *status* explanation..

.. attribute:: started
.. attribute:: prepared

Read-only :class:`bool` property, ``True`` if :meth:`start` has
Read-only :class:`bool` property, ``True`` if :meth:`prepare` has
been called, ``False`` otherwise.

.. versionadded:: 0.18

.. attribute:: started

Deprecated alias for :attr:`prepared`.

.. deprecated:: 0.18

.. attribute:: status

Read-only property for *HTTP response status code*, :class:`int`.
Expand Down Expand Up @@ -550,16 +557,30 @@ StreamResponse
Send *HTTP header*. You should not change any header data after
calling this method.

.. deprecated:: 0.18

Use :meth:`prepare` instead.

.. coroutinemethod:: prepare(request)

:param aiohttp.web.Request request: HTTP request object, that the
response answers.

Send *HTTP header*. You should not change any header data after
calling this method.

.. versionadded:: 0.18

.. method:: write(data)

Send byte-ish data as the part of *response BODY*.

:meth:`start` must be called before.
:meth:`prepare` must be called before.

Raises :exc:`TypeError` if data is not :class:`bytes`,
:class:`bytearray` or :class:`memoryview` instance.

Raises :exc:`RuntimeError` if :meth:`start` has not been called.
Raises :exc:`RuntimeError` if :meth:`prepare` has not been called.

Raises :exc:`RuntimeError` if :meth:`write_eof` has been called.

Expand Down Expand Up @@ -651,11 +672,23 @@ WebSocketResponse

Class for handling server-side websockets.

After starting (by :meth:`start` call) the response you
After starting (by :meth:`prepare` call) the response you
cannot use :meth:`~StreamResponse.write` method but should to
communicate with websocket client by :meth:`send_str`,
:meth:`receive` and others.

.. coroutinemethod:: prepare(request)

Starts websocket. After the call you can use websocket methods.

:param aiohttp.web.Request request: HTTP request object, that the
response answers.


:raises HTTPException: if websocket handshake has failed.

.. versionadded:: 0.18

.. method:: start(request)

Starts websocket. After the call you can use websocket methods.
Expand All @@ -666,12 +699,17 @@ WebSocketResponse

:raises HTTPException: if websocket handshake has failed.

.. method:: can_start(request)
.. deprecated:: 0.18

Use :meth:`prepare` instead.

.. method:: can_prepare(request)

Performs checks for *request* data to figure out if websocket
can be started on the request.

If :meth:`can_start` call is success then :meth:`start` will success too.
If :meth:`can_prepare` call is success then :meth:`prepare` will
success too.

:param aiohttp.web.Request request: HTTP request object, that the
response answers.
Expand All @@ -684,6 +722,12 @@ WebSocketResponse

.. note:: The method never raises exception.

.. method:: can_start(request)

Deprecated alias for :meth:`can_prepare`

.. deprecated:: 0.18

.. attribute:: closed

Read-only property, ``True`` if connection has been closed or in process
Expand Down
4 changes: 2 additions & 2 deletions examples/web_srv.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def intro(request):
binary = txt.encode('utf8')
resp = StreamResponse()
resp.content_length = len(binary)
resp.start(request)
yield from resp.prepare(request)
resp.write(binary)
return resp

Expand All @@ -36,7 +36,7 @@ def hello(request):
name = request.match_info.get('name', 'Anonymous')
answer = ('Hello, ' + name).encode('utf8')
resp.content_length = len(answer)
resp.start(request)
yield from resp.prepare(request)
resp.write(answer)
yield from resp.write_eof()
return resp
Expand Down
2 changes: 1 addition & 1 deletion examples/web_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def wshandler(request):
with open(WS_FILE, 'rb') as fp:
return Response(body=fp.read(), content_type='text/html')

resp.start(request)
yield from resp.prepare(request)
print('Someone joined.')
for ws in request.app['sockets']:
ws.send_str('Someone joined')
Expand Down
2 changes: 1 addition & 1 deletion tests/autobahn/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def wshandler(request):
if not ok:
return web.HTTPBadRequest()

ws.start(request)
yield from ws.prepare(request)

while True:
msg = yield from ws.receive()
Expand Down
Loading