Skip to content

Commit

Permalink
Automatic NoContentResponse from None (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
ondratu committed Oct 13, 2024
1 parent a3b1c1e commit 0db4a21
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 17 deletions.
5 changes: 3 additions & 2 deletions doc/ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* Right HTTPException type annotation
* Fix ETag header
* FieldStorage and FieldStorageParser implementation based on legacy
cgi.FieldStorage
- that means Python 3.13 support
cgi.FieldStorage (#22 )
- that means Python 3.13 support (#36)
* Stop Python 3.8 support - some mypy annotation didn't work
* Field refactoring - ! possible break change ! - sorry for that
- FieldStorageInterface - getvalue, getfist, getlist methods
Expand All @@ -14,6 +14,7 @@
as deprecated, and will be deleted in next major version.
* Automatic JSONResponse from dict or list, just like from text.
* Debug info, documentation and Error improvements.
* Automatic NoContentResponse from None (#28)

==== 2.6.3 ====
* HTTPException has status_code property
Expand Down
28 changes: 26 additions & 2 deletions doc/documentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,15 @@ make_response function.
headers=None, status_code=HTTP_OK)
data : str, bytes, generator
Returned value as response body.
data : str, bytes, dict, list, None or generator
Returned value as response body. Each type of data returns different
response type:

- str, bytes - Response
- dict, list - JSONResponse
- None - NoContentReponse
- generator - GeneratorResponse

content_type : str
The ``Content-Type`` header which is set, if this header is not set
in headers.
Expand All @@ -127,6 +134,23 @@ You can use headers instead of `content_type` argument.
headers={"Content-Type": "text/plain"},
status_code=NOT_FOUND)
If you return just simple type, or tuple of arguments, PoorWSGI automatically
call make_response function to create response for you.

.. code:: python
@app.route("/json")
def not_found(req):
"""Return JSONResponse"""
return {"msg": "Message", "type": "object"}
@app.route('/gone')
def gone(req):
"""Return NoContentResponse"""
return None, "", None, HTTP_GONE
Response
````````
Response object is one of base element of WSGI application. Response is object
Expand Down
4 changes: 2 additions & 2 deletions examples/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,8 +676,8 @@ def method_raises_errror(_):
raise RuntimeError('Test of internal server error')


@app.route('/none-error')
def none_error_handler(_):
@app.route('/none')
def none_no_content(_):
"""Test for None response."""


Expand Down
31 changes: 25 additions & 6 deletions poorwsgi/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ def status_code(self):
return self.args[0].status_code


def make_response(data: Union[str, bytes, dict, Iterable[bytes]],
def make_response(data: Optional[Union[str, bytes, dict, Iterable[bytes]]],
content_type: str = "text/html; charset=utf-8",
headers: Optional[Union[Headers, HeadersList]] = None,
status_code: int = HTTP_OK):
Expand Down Expand Up @@ -851,17 +851,19 @@ def make_response(data: Union[str, bytes, dict, Iterable[bytes]],
>>> res.headers["OK-Header"]
'OK'
JSONResponse from dictionary:
JSONResponse from dictionary, content_type argument is ignored:
>>> res = make_response({"key": "value"})
>>> res = make_response({"key": "value"}, "text/plain", status_code=201)
>>> res
<poorwsgi.response.JSONResponse object at ...>
>>> res.data
b'{"key": "value"}'
>>> res.content_type
'application/json; charset=utf-8'
>>> res.status_code
201
JSONResponse from list:
JSONResponse from list, content_type argument is ignored:
>>> res = make_response([["key", "value"]])
>>> res
Expand All @@ -879,14 +881,31 @@ def make_response(data: Union[str, bytes, dict, Iterable[bytes]],
>>> list(res.__end_of_response__())
[b'key', b'value']
NoContentResponse from None, content_type argument is ignored. If
status_code is leave 200 OK, then status will be 204 No Content:
>>> res = make_response(None)
>>> res
<poorwsgi.response.NoContentResponse object at ...>
>>> res.status_code
204
>>> res = make_response(None, status_code=201)
>>> res.status_code
201
"""
try:
if isinstance(data, (str, bytes)): # "hello world"
return Response(data, content_type, headers, status_code)
if isinstance(data, dict):
return JSONResponse(data)
return JSONResponse(data, headers=headers, status_code=status_code)
if isinstance(data, list) and not isinstance(data[0], bytes):
return JSONResponse(data)
return JSONResponse(data, headers=headers, status_code=status_code)
if data is None:
if status_code == HTTP_OK:
status_code = HTTP_NO_CONTENT
return NoContentResponse(headers=headers, status_code=status_code)

iter(data) # try iter data
return GeneratorResponse(data, content_type, headers, status_code)
Expand Down
9 changes: 4 additions & 5 deletions tests_integrity/test_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ def test_file_response_304_etag(self, url):
headers={'If-None-Match': etag},
status_code=304)

def test_none_no_content(self, url):
"""Test debug output - which handler crash on none result."""
check_url(url+"/none", status_code=204)


class TestPartialResponse():
"""Tests for Partial Responses"""
Expand Down Expand Up @@ -219,11 +223,6 @@ class TestErrors():
def test_internal_server_error(self, url):
check_url(url+"/internal-server-error", status_code=500)

def test_none_error(self, url):
"""Test debug output - which handler crash on none result."""
res = check_url(url+"/none-error", status_code=500)
assert "none_error_handler" in res.text

def test_bad_request(self, url):
check_url(url+"/bad-request", status_code=400)

Expand Down

0 comments on commit 0db4a21

Please sign in to comment.