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.
Files changed (61) hide show
  1. {yhttp-8.2.1 → yhttp-8.3.0}/PKG-INFO +1 -1
  2. yhttp-8.3.0/tests/test_middleware.py +48 -0
  3. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_request.py +1 -1
  4. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/application.py +16 -7
  5. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/request.py +21 -15
  6. yhttp-8.3.0/yhttp/core/version.py +1 -0
  7. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp.egg-info/PKG-INFO +1 -1
  8. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp.egg-info/SOURCES.txt +1 -0
  9. yhttp-8.2.1/yhttp/core/version.py +0 -1
  10. {yhttp-8.2.1 → yhttp-8.3.0}/LICENSE +0 -0
  11. {yhttp-8.2.1 → yhttp-8.3.0}/README.md +0 -0
  12. {yhttp-8.2.1 → yhttp-8.3.0}/setup.cfg +0 -0
  13. {yhttp-8.2.1 → yhttp-8.3.0}/setup.py +0 -0
  14. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_application.py +0 -0
  15. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_applicationcli.py +0 -0
  16. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_builtincli_serve.py +0 -0
  17. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_builtincli_version.py +0 -0
  18. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_builtincli_yhttp_version.py +0 -0
  19. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_cookie.py +0 -0
  20. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_cookieset.py +0 -0
  21. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_extension.py +0 -0
  22. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_form.py +0 -0
  23. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_form_json.py +0 -0
  24. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_form_multipart.py +0 -0
  25. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_form_urlencoded.py +0 -0
  26. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_fswatcher.py +0 -0
  27. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_guard.py +0 -0
  28. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_guard_body.py +0 -0
  29. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_guard_contenttypes.py +0 -0
  30. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_guard_query.py +0 -0
  31. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_headerset.py +0 -0
  32. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_jsonencoding.py +0 -0
  33. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_locale.py +0 -0
  34. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_multidict.py +0 -0
  35. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_multipart.py +0 -0
  36. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_querystring.py +0 -0
  37. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_responseheaders.py +0 -0
  38. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_rewrite.py +0 -0
  39. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_rewritecli.py +0 -0
  40. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_routing.py +0 -0
  41. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_static.py +0 -0
  42. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_statuses.py +0 -0
  43. {yhttp-8.2.1 → yhttp-8.3.0}/tests/test_statushandler.py +0 -0
  44. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/__init__.py +0 -0
  45. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/cli.py +0 -0
  46. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/contenttypes.py +0 -0
  47. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/cookieset.py +0 -0
  48. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/fswatcher.py +0 -0
  49. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/guard.py +0 -0
  50. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/headerset.py +0 -0
  51. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/main.py +0 -0
  52. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/multidict.py +0 -0
  53. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/multipart.py +0 -0
  54. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/response.py +0 -0
  55. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/rewrite.py +0 -0
  56. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/static.py +0 -0
  57. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp/core/statuses.py +0 -0
  58. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp.egg-info/dependency_links.txt +0 -0
  59. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp.egg-info/entry_points.txt +0 -0
  60. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp.egg-info/requires.txt +0 -0
  61. {yhttp-8.2.1 → yhttp-8.3.0}/yhttp.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yhttp
3
- Version: 8.2.1
3
+ Version: 8.3.0
4
4
  Summary: A very micro http framework.
5
5
  Home-page: http://github.com/yhttp/yhttp
6
6
  Author: Vahid Mardani
@@ -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 == 'http://bddrest-interceptor/foo?bar=baz'
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 isinstance(body, statuses.HTTPStatus):
306
- raise body
304
+ # execute middlewares if any
305
+ for middleware in self.middlewares:
306
+ body = middleware(request)
307
+ if body:
308
+ break
307
309
 
308
- if isinstance(body, types.GeneratorType):
309
- response.stream_firstchunk = next(body)
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
- @cached_property
101
+ @property
87
102
  def fullpath(self):
88
103
  """Request full URI including query string."""
89
- return wsgiutil.request_uri(self.environ, include_query=True)
104
+ result = self.path
105
+ query = urlencode(self.query, doseq=True)
106
+ if query:
107
+ result += f'?{query}'
90
108
 
91
- @cached_property
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yhttp
3
- Version: 8.2.1
3
+ Version: 8.3.0
4
4
  Summary: A very micro http framework.
5
5
  Home-page: http://github.com/yhttp/yhttp
6
6
  Author: Vahid Mardani
@@ -21,6 +21,7 @@ tests/test_guard_query.py
21
21
  tests/test_headerset.py
22
22
  tests/test_jsonencoding.py
23
23
  tests/test_locale.py
24
+ tests/test_middleware.py
24
25
  tests/test_multidict.py
25
26
  tests/test_multipart.py
26
27
  tests/test_querystring.py
@@ -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