yhttp 8.2.1__tar.gz → 8.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {yhttp-8.2.1 → yhttp-8.3.0}/PKG-INFO +1 -1
- yhttp-8.3.0/tests/test_middleware.py +48 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_request.py +1 -1
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/application.py +16 -7
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/request.py +21 -15
- yhttp-8.3.0/yhttp/core/version.py +1 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp.egg-info/PKG-INFO +1 -1
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp.egg-info/SOURCES.txt +1 -0
- yhttp-8.2.1/yhttp/core/version.py +0 -1
- {yhttp-8.2.1 → yhttp-8.3.0}/LICENSE +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/README.md +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/setup.cfg +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/setup.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_application.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_applicationcli.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_builtincli_serve.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_builtincli_version.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_builtincli_yhttp_version.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_cookie.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_cookieset.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_extension.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_form.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_form_json.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_form_multipart.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_form_urlencoded.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_fswatcher.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_guard.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_guard_body.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_guard_contenttypes.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_guard_query.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_headerset.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_jsonencoding.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_locale.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_multidict.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_multipart.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_querystring.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_responseheaders.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_rewrite.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_rewritecli.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_routing.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_static.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_statuses.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_statushandler.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/__init__.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/cli.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/contenttypes.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/cookieset.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/fswatcher.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/guard.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/headerset.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/main.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/multidict.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/multipart.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/response.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/rewrite.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/static.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/statuses.py +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp.egg-info/dependency_links.txt +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp.egg-info/entry_points.txt +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp.egg-info/requires.txt +0 -0
- {yhttp-8.2.1 → yhttp-8.3.0}/yhttp.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from bddrest import status, response
|
|
2
|
+
|
|
3
|
+
from yhttp.core import statuses
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_middleware_returnnone(app, httpreq):
|
|
7
|
+
def middleware(request):
|
|
8
|
+
request.query['baz'] = 'qux'
|
|
9
|
+
|
|
10
|
+
app.middlewares.append(middleware)
|
|
11
|
+
|
|
12
|
+
@app.route()
|
|
13
|
+
def get(req):
|
|
14
|
+
return '&'.join([f'{k}={v}' for k, v in req.query.items()])
|
|
15
|
+
|
|
16
|
+
with httpreq('?foo=bar'):
|
|
17
|
+
assert status == 200
|
|
18
|
+
assert response.text == 'foo=bar&baz=qux'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_middleware_returnbody(app, httpreq):
|
|
22
|
+
def middleware(request):
|
|
23
|
+
return 'something else'
|
|
24
|
+
|
|
25
|
+
app.middlewares.append(middleware)
|
|
26
|
+
|
|
27
|
+
@app.route()
|
|
28
|
+
def get(req):
|
|
29
|
+
return 'foobar'
|
|
30
|
+
|
|
31
|
+
with httpreq('?foo=bar'):
|
|
32
|
+
assert status == 200
|
|
33
|
+
assert response.text == 'something else'
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_middleware_raise(app, httpreq):
|
|
37
|
+
def middleware(request):
|
|
38
|
+
raise statuses.found('/foo')
|
|
39
|
+
|
|
40
|
+
app.middlewares.append(middleware)
|
|
41
|
+
|
|
42
|
+
@app.route()
|
|
43
|
+
def get(req):
|
|
44
|
+
return 'foobar'
|
|
45
|
+
|
|
46
|
+
with httpreq('?foo=bar'):
|
|
47
|
+
assert status == 302
|
|
48
|
+
assert response.headers['location'] == '/foo'
|
|
@@ -4,7 +4,7 @@ from bddrest import status
|
|
|
4
4
|
def test_request(app, httpreq):
|
|
5
5
|
@app.route('/foo')
|
|
6
6
|
def get(req):
|
|
7
|
-
assert req.fullpath == '
|
|
7
|
+
assert req.fullpath == '/foo?bar=baz'
|
|
8
8
|
assert req.scheme == 'http'
|
|
9
9
|
assert req.headers.get('foo-bar') == 'baz'
|
|
10
10
|
assert req.contenttype is None
|
|
@@ -62,6 +62,7 @@ class BaseApplication:
|
|
|
62
62
|
self.events = {}
|
|
63
63
|
self.cliarguments = []
|
|
64
64
|
self.settings = pymlconf.Root(self._builtinsettings)
|
|
65
|
+
self.middlewares = []
|
|
65
66
|
|
|
66
67
|
def when(self, func):
|
|
67
68
|
"""Return decorator to registers the ``func`` into :attr:`events` by
|
|
@@ -296,17 +297,25 @@ class Application(BaseApplication):
|
|
|
296
297
|
|
|
297
298
|
"""
|
|
298
299
|
response = self.response_factory(self, environ, startresponse)
|
|
300
|
+
request = self.request_factory(self, environ, response)
|
|
299
301
|
|
|
300
302
|
try:
|
|
301
|
-
request = self.request_factory(self, environ, response)
|
|
302
|
-
handler, pathparams, info = self._findhandler(request)
|
|
303
|
-
body = handler(request, *pathparams, **info['kwonly'])
|
|
304
303
|
|
|
305
|
-
if
|
|
306
|
-
|
|
304
|
+
# execute middlewares if any
|
|
305
|
+
for middleware in self.middlewares:
|
|
306
|
+
body = middleware(request)
|
|
307
|
+
if body:
|
|
308
|
+
break
|
|
307
309
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
+
else:
|
|
311
|
+
handler, pathparams, info = self._findhandler(request)
|
|
312
|
+
body = handler(request, *pathparams, **info['kwonly'])
|
|
313
|
+
|
|
314
|
+
if isinstance(body, statuses.HTTPStatus):
|
|
315
|
+
raise body
|
|
316
|
+
|
|
317
|
+
if isinstance(body, types.GeneratorType):
|
|
318
|
+
response.stream_firstchunk = next(body)
|
|
310
319
|
|
|
311
320
|
response.body = body
|
|
312
321
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import wsgiref.util as wsgiutil
|
|
2
|
+
from urllib.parse import urlencode
|
|
2
3
|
from http import cookies
|
|
3
4
|
from functools import partial, cached_property
|
|
4
5
|
from urllib.parse import parse_qs, unquote, quote
|
|
@@ -72,6 +73,20 @@ class Request:
|
|
|
72
73
|
"""HTTP method."""
|
|
73
74
|
return self.environ['REQUEST_METHOD'].lower()
|
|
74
75
|
|
|
76
|
+
@cached_property
|
|
77
|
+
def contentlength(self):
|
|
78
|
+
"""HTTP Request ``Content-Length`` header value."""
|
|
79
|
+
v = self.environ.get('CONTENT_LENGTH')
|
|
80
|
+
return None if not v or not v.strip() else int(v)
|
|
81
|
+
|
|
82
|
+
@cached_property
|
|
83
|
+
def contenttype(self):
|
|
84
|
+
"""HTTP Request ``Content-Type`` header value without encoding."""
|
|
85
|
+
contenttype = self.environ.get('CONTENT_TYPE')
|
|
86
|
+
if contenttype:
|
|
87
|
+
return contenttype.split(';')[0]
|
|
88
|
+
return None
|
|
89
|
+
|
|
75
90
|
@cached_property
|
|
76
91
|
def path(self):
|
|
77
92
|
"""Request URL without query string and ``scheme://domain.ext``."""
|
|
@@ -83,24 +98,15 @@ class Request:
|
|
|
83
98
|
|
|
84
99
|
return p
|
|
85
100
|
|
|
86
|
-
@
|
|
101
|
+
@property
|
|
87
102
|
def fullpath(self):
|
|
88
103
|
"""Request full URI including query string."""
|
|
89
|
-
|
|
104
|
+
result = self.path
|
|
105
|
+
query = urlencode(self.query, doseq=True)
|
|
106
|
+
if query:
|
|
107
|
+
result += f'?{query}'
|
|
90
108
|
|
|
91
|
-
|
|
92
|
-
def contentlength(self):
|
|
93
|
-
"""HTTP Request ``Content-Length`` header value."""
|
|
94
|
-
v = self.environ.get('CONTENT_LENGTH')
|
|
95
|
-
return None if not v or not v.strip() else int(v)
|
|
96
|
-
|
|
97
|
-
@cached_property
|
|
98
|
-
def contenttype(self):
|
|
99
|
-
"""HTTP Request ``Content-Type`` header value without encoding."""
|
|
100
|
-
contenttype = self.environ.get('CONTENT_TYPE')
|
|
101
|
-
if contenttype:
|
|
102
|
-
return contenttype.split(';')[0]
|
|
103
|
-
return None
|
|
109
|
+
return result
|
|
104
110
|
|
|
105
111
|
@cached_property
|
|
106
112
|
def query(self):
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '8.3.0'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '8.2.1'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|