Make content a positional argument
[fwx] / phial.py
1 import collections
2
3 Request = collections.namedtuple(
4     'Request',
5     (
6         'environ',
7     )
8 )
9
10 _Response = collections.namedtuple(
11     'Response',
12     (
13         'status',
14         'content_type',
15         'extra_headers',
16         'content',
17     ),
18 )
19
20 class Response(_Response):
21     def __new__(cls, content, **kwargs):
22         status = kwargs.pop('status', 200)
23         assert isinstance(status, int)
24
25         content_type = kwargs.pop('content_type')
26         assert isinstance(content_type, str)
27
28         extra_headers = kwargs.pop('extra_headers', ())
29         assert isinstance(extra_headers, tuple)
30
31         assert len(kwargs) == 0
32
33         return super().__new__(
34             cls,
35             status=status,
36             content_type=content_type,
37             extra_headers=extra_headers,
38             content=content,
39         )
40
41     @property
42     def headers(self):
43         return (
44             ('Content-Type', self.content_type),
45         )
46
47 class TextResponse(Response):
48     def __new__(cls, content, **kwargs):
49         assert 'content_type' not in kwargs
50         return super().__new__(
51             cls,
52             content,
53             content_type='text/plain',
54             **kwargs,
55         )
56
57 def _get_status(response):
58     return {
59         200: '200 OK',
60     }[response.status]
61
62 def _get_headers(response):
63     return list(response.headers)
64
65 def _get_content(response):
66     content = response.content
67
68     if isinstance(content, bytes):
69         return (content,)
70
71     if isinstance(content, str):
72         return (content.encode('utf-8'),)
73
74     return content
75
76 def App(handler):
77     def app(environ, start_fn):
78         response = handler(Request(environ))
79
80         start_fn(_get_status(response), _get_headers(response))
81         return _get_content(response)
82     return app