Skip to content

Commit 39f7122

Browse files
committed
Merge branch 'master' into multidict-error
Conflicts: tests/test_multidict.py
2 parents 0af33f9 + a084fde commit 39f7122

21 files changed

+250
-164
lines changed

.coveragerc

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,5 @@ branch = True
33
source = aiohttp, tests
44
omit = site-packages
55

6-
[report]
7-
exclude_lines =
8-
@abc.
9-
106
[html]
117
directory = coverage

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ install:
99
- pip install flake8
1010
- pip install docutils
1111
- pip install coverage
12+
- pip install gunicorn
13+
- pip install chardet
1214
- python setup.py develop
1315

1416
script:

CHANGES.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ CHANGES
3737

3838
- Allow to send raw chunked encoded response.
3939

40+
- Allow to encode output bytes stream into chunked encoding.
41+
42+
- Allow to compress output bytes stream with `deflate` encoding.
43+
44+
- Server has 75 seconds keepalive timeout now, was non-keepalive by default.
45+
46+
- Application doesn't accept `**kwargs` anymore (#243).
47+
48+
- Request is inherited from dict now for making per-request storage to
49+
middlewares (#242).
50+
4051

4152
0.13.1 (12-31-2014)
4253
--------------------

aiohttp/_multidict.pyx

Lines changed: 24 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,8 @@ class upstr(str):
1515
val = str(val, encoding, errors)
1616
elif isinstance(val, str):
1717
pass
18-
elif hasattr(val, '__str__'):
19-
val = val.__str__()
2018
else:
21-
val = repr(val)
19+
val = str(val)
2220
val = val.upper()
2321
return str.__new__(cls, val)
2422

@@ -109,53 +107,30 @@ cdef class _Base:
109107
body = ', '.join("'{}': {!r}".format(k, v) for k, v in self.items())
110108
return '<{} {{{}}}>'.format(self.__class__.__name__, body)
111109

112-
113-
114-
115-
116-
cdef class MultiDictProxy(_Base):
117-
118-
def __init__(self, arg):
119-
cdef MultiDict mdict
120-
if not isinstance(arg, MultiDict):
121-
raise TypeError(
122-
'MultiDictProxy requires MultiDict instance, not {}'.format(
123-
type(arg)))
124-
125-
mdict = arg
126-
self._items = mdict._items
127-
128-
def copy(self):
129-
return MultiDict(self._items)
130-
131110
def __richcmp__(self, other, op):
132-
cdef MultiDictProxy typed_self = self
133-
cdef MultiDictProxy typed_other
111+
cdef _Base typed_self
112+
cdef _Base typed_other
134113
cdef tuple item
135114
if op == 2:
136-
if isinstance(other, MultiDictProxy):
137-
typed_other = other
138-
return typed_self._items == typed_other._items
139-
elif isinstance(other, MultiDict):
115+
if isinstance(self, _Base) and isinstance(other, _Base):
116+
typed_self = self
140117
typed_other = other
141118
return typed_self._items == typed_other._items
142119
elif not isinstance(other, abc.Mapping):
143120
return NotImplemented
144-
for item in typed_self._items:
121+
for item in self.items():
145122
nv = other.get(item[0], _marker)
146123
if item[1] != nv:
147124
return False
148125
return True
149126
elif op != 2:
150-
if isinstance(other, MultiDictProxy):
127+
if isinstance(self, _Base) and isinstance(other, _Base):
128+
typed_self = self
151129
typed_other = other
152130
return typed_self._items != typed_other._items
153-
elif isinstance(other, MultiDict):
154-
typed_other = other
155-
return typed_self._items == typed_other._items
156131
elif not isinstance(other, abc.Mapping):
157132
return NotImplemented
158-
for item in typed_self._items:
133+
for item in self.items():
159134
nv = other.get(item[0], _marker)
160135
if item[1] == nv:
161136
return True
@@ -164,6 +139,21 @@ cdef class MultiDictProxy(_Base):
164139
return NotImplemented
165140

166141

142+
cdef class MultiDictProxy(_Base):
143+
144+
def __init__(self, arg):
145+
cdef MultiDict mdict
146+
if not isinstance(arg, MultiDict):
147+
raise TypeError(
148+
'MultiDictProxy requires MultiDict instance, not {}'.format(
149+
type(arg)))
150+
151+
mdict = arg
152+
self._items = mdict._items
153+
154+
def copy(self):
155+
return MultiDict(self._items)
156+
167157
abc.Mapping.register(MultiDictProxy)
168158

169159

@@ -331,36 +321,6 @@ cdef class MultiDict(_Base):
331321
def update(self, *args, **kwargs):
332322
self._extend(args, kwargs, "update", 0)
333323

334-
def __richcmp__(self, other, op):
335-
cdef MultiDict typed_self = self
336-
cdef MultiDict typed_other
337-
cdef tuple item
338-
if op == 2:
339-
if isinstance(other, MultiDict):
340-
typed_other = other
341-
return typed_self._items == typed_other._items
342-
elif not isinstance(other, abc.Mapping):
343-
return NotImplemented
344-
for item in typed_self._items:
345-
nv = other.get(item[0], _marker)
346-
if item[1] != nv:
347-
return False
348-
return True
349-
elif op != 2:
350-
if isinstance(other, MultiDict):
351-
typed_other = other
352-
return typed_self._items == typed_other._items
353-
elif not isinstance(other, abc.Mapping):
354-
return NotImplemented
355-
for item in typed_self._items:
356-
nv = other.get(item[0], _marker)
357-
if item[1] == nv:
358-
return True
359-
return False
360-
else:
361-
return NotImplemented
362-
363-
364324

365325
abc.MutableMapping.register(MultiDict)
366326

aiohttp/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@ def _get_encoding(self, encoding):
742742
encoding = params.get('charset')
743743
if not encoding and chardet:
744744
encoding = chardet.detect(self._content)['encoding']
745-
if not encoding: # pragma: no cover
745+
if not encoding:
746746
encoding = 'utf-8'
747747

748748
return encoding

aiohttp/multidict.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@ def __new__(cls, val='',
1616
val = str(val, encoding, errors)
1717
elif isinstance(val, str):
1818
pass
19-
elif hasattr(val, '__str__'):
20-
val = val.__str__()
2119
else:
22-
val = repr(val)
20+
val = str(val)
2321
val = val.upper()
2422
return str.__new__(cls, val)
2523

@@ -79,9 +77,7 @@ def values(self):
7977
def __eq__(self, other):
8078
if not isinstance(other, abc.Mapping):
8179
return NotImplemented
82-
if isinstance(other, _MultiDictProxy):
83-
return self._items == other._items
84-
elif isinstance(other, _MultiDict):
80+
if isinstance(other, _Base):
8581
return self._items == other._items
8682
for k, v in self.items():
8783
nv = other.get(k, _marker)
@@ -206,11 +202,7 @@ def clear(self):
206202
# Mapping interface #
207203

208204
def __setitem__(self, key, value):
209-
try:
210-
del self[key]
211-
except KeyError:
212-
pass
213-
self._items.append((key, value))
205+
self._replace(key, value)
214206

215207
def __delitem__(self, key):
216208
items = self._items
@@ -333,7 +325,7 @@ def __iter__(self):
333325
MultiDict,
334326
CIMultiDict,
335327
upstr)
336-
except ImportError:
328+
except ImportError: # pragma: no cover
337329
MultiDictProxy = _MultiDictProxy
338330
CIMultiDictProxy = _CIMultiDictProxy
339331
MultiDict = _MultiDict

aiohttp/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class ServerHttpProtocol(aiohttp.StreamProtocol):
7575
_request_parser = aiohttp.HttpRequestParser() # default request parser
7676

7777
def __init__(self, *, loop=None,
78-
keep_alive=None,
78+
keep_alive=75, # NGINX default value is 75 secs
7979
timeout=15,
8080
tcp_keepalive=True,
8181
allowed_methods=(),

aiohttp/web.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def content_length(self, _CONTENT_LENGTH=hdrs.CONTENT_LENGTH):
139139
############################################################
140140

141141

142-
class Request(HeadersMixin):
142+
class Request(dict, HeadersMixin):
143143

144144
def __init__(self, app, message, payload, transport, reader, writer, *,
145145
_HOST=hdrs.HOST):
@@ -1553,7 +1553,7 @@ class Application(dict):
15531553

15541554
def __init__(self, *, logger=web_logger, loop=None,
15551555
router=None, handler_factory=RequestHandlerFactory,
1556-
middlewares=(), **kwargs):
1556+
middlewares=()):
15571557
if loop is None:
15581558
loop = asyncio.get_event_loop()
15591559
if router is None:
@@ -1566,7 +1566,6 @@ def __init__(self, *, logger=web_logger, loop=None,
15661566
self._loop = loop
15671567
self.logger = logger
15681568

1569-
self.update(**kwargs)
15701569
for factory in middlewares:
15711570
assert asyncio.iscoroutinefunction(factory), factory
15721571
self._middlewares = tuple(middlewares)

docs/index.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@ Library Installation
2323

2424
::
2525

26-
pip3 install aiohttp
26+
pip install aiohttp
2727

2828
For smart detection of *Content-Type* by client API you would like to
29-
install *chardet* also::
29+
install `chardet` also::
3030

3131
pip install chardet
3232

33+
*Optional*: To improve performances, you can install `Cython`
34+
**before** `aiohttp`::
35+
36+
pip install cython
3337

3438
Getting Started
3539
---------------

docs/web.rst

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,48 @@ so application developer can use classes if he wants::
127127
app.router.add_route('GET', '/intro', handler.handle_intro)
128128
app.router.add_route('GET', '/greet/{name}', handler.handle_greeting)
129129

130+
Custom conditions for routes lookup
131+
-----------------------------------
132+
133+
Sometimes you need to distinguish *web-handlers* on more complex
134+
criteria than *HTTP method* and *path*.
135+
136+
While :class:`UrlDispatcher` doesn't accept extra criterias there is
137+
easy way to do the task by implementing the second routing layer by
138+
hands.
139+
140+
The example shows custom processing based on *HTTP Accept* header::
141+
142+
class Handler:
143+
144+
def __init__(self):
145+
self._accepts = {}
146+
147+
@asyncio.coroutine
148+
def do_route(self, request):
149+
acceptor = self._accepts.get(request.headers.get('ACCEPT'))
150+
if acceptor is None:
151+
raise HTTPNotAcceptable()
152+
return (yield from acceptor(request))
153+
154+
def reg_acceptor(self, accept, handler):
155+
self._accepts[accept] = handler
156+
157+
158+
@asyncio.coroutine
159+
def handle_json(request):
160+
# do json handling
161+
162+
@asyncio.coroutine
163+
def handle_xml(request):
164+
# do xml handling
165+
166+
handler = Handler()
167+
app.router.add_route('GET', '/', handler.do_route)
168+
handler.reg_acceptor('application/json', handle_json)
169+
handler.reg_acceptor('application/xml', handle_xml)
170+
171+
130172

131173
.. _aiohttp-web-file-upload:
132174

0 commit comments

Comments
 (0)