microdot 2.3.5__tar.gz → 2.4.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 (73) hide show
  1. {microdot-2.3.5/src/microdot.egg-info → microdot-2.4.0}/PKG-INFO +1 -1
  2. {microdot-2.3.5 → microdot-2.4.0}/pyproject.toml +1 -1
  3. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/__init__.py +2 -0
  4. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/microdot.py +11 -2
  5. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/session.py +4 -1
  6. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/sse.py +19 -6
  7. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/test_client.py +6 -2
  8. {microdot-2.3.5 → microdot-2.4.0/src/microdot.egg-info}/PKG-INFO +1 -1
  9. {microdot-2.3.5 → microdot-2.4.0}/src/microdot.egg-info/SOURCES.txt +0 -1
  10. {microdot-2.3.5 → microdot-2.4.0}/tests/test_microdot.py +2 -1
  11. {microdot-2.3.5 → microdot-2.4.0}/tests/test_sse.py +19 -12
  12. microdot-2.3.5/src/microdot/pubsub.py +0 -21
  13. {microdot-2.3.5 → microdot-2.4.0}/LICENSE +0 -0
  14. {microdot-2.3.5 → microdot-2.4.0}/MANIFEST.in +0 -0
  15. {microdot-2.3.5 → microdot-2.4.0}/README.md +0 -0
  16. {microdot-2.3.5 → microdot-2.4.0}/docs/Makefile +0 -0
  17. {microdot-2.3.5 → microdot-2.4.0}/docs/_static/css/custom.css +0 -0
  18. {microdot-2.3.5 → microdot-2.4.0}/docs/api.rst +0 -0
  19. {microdot-2.3.5 → microdot-2.4.0}/docs/conf.py +0 -0
  20. {microdot-2.3.5 → microdot-2.4.0}/docs/extensions.rst +0 -0
  21. {microdot-2.3.5 → microdot-2.4.0}/docs/freezing.rst +0 -0
  22. {microdot-2.3.5 → microdot-2.4.0}/docs/index.rst +0 -0
  23. {microdot-2.3.5 → microdot-2.4.0}/docs/intro.rst +0 -0
  24. {microdot-2.3.5 → microdot-2.4.0}/docs/make.bat +0 -0
  25. {microdot-2.3.5 → microdot-2.4.0}/docs/migrating.rst +0 -0
  26. {microdot-2.3.5 → microdot-2.4.0}/setup.cfg +0 -0
  27. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/asgi.py +0 -0
  28. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/auth.py +0 -0
  29. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/cors.py +0 -0
  30. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/helpers.py +0 -0
  31. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/jinja.py +0 -0
  32. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/login.py +0 -0
  33. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/multipart.py +0 -0
  34. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/utemplate.py +0 -0
  35. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/websocket.py +0 -0
  36. {microdot-2.3.5 → microdot-2.4.0}/src/microdot/wsgi.py +0 -0
  37. {microdot-2.3.5 → microdot-2.4.0}/src/microdot.egg-info/dependency_links.txt +0 -0
  38. {microdot-2.3.5 → microdot-2.4.0}/src/microdot.egg-info/not-zip-safe +0 -0
  39. {microdot-2.3.5 → microdot-2.4.0}/src/microdot.egg-info/requires.txt +0 -0
  40. {microdot-2.3.5 → microdot-2.4.0}/src/microdot.egg-info/top_level.txt +0 -0
  41. {microdot-2.3.5 → microdot-2.4.0}/tests/__init__.py +0 -0
  42. {microdot-2.3.5 → microdot-2.4.0}/tests/files/test.bin +0 -0
  43. {microdot-2.3.5 → microdot-2.4.0}/tests/files/test.css +0 -0
  44. {microdot-2.3.5 → microdot-2.4.0}/tests/files/test.gif +0 -0
  45. {microdot-2.3.5 → microdot-2.4.0}/tests/files/test.gz +0 -0
  46. {microdot-2.3.5 → microdot-2.4.0}/tests/files/test.html +0 -0
  47. {microdot-2.3.5 → microdot-2.4.0}/tests/files/test.jpg +0 -0
  48. {microdot-2.3.5 → microdot-2.4.0}/tests/files/test.js +0 -0
  49. {microdot-2.3.5 → microdot-2.4.0}/tests/files/test.json +0 -0
  50. {microdot-2.3.5 → microdot-2.4.0}/tests/files/test.png +0 -0
  51. {microdot-2.3.5 → microdot-2.4.0}/tests/files/test.txt +0 -0
  52. {microdot-2.3.5 → microdot-2.4.0}/tests/files/test.txt.gz +0 -0
  53. {microdot-2.3.5 → microdot-2.4.0}/tests/mock_socket.py +0 -0
  54. {microdot-2.3.5 → microdot-2.4.0}/tests/templates/hello.jinja.txt +0 -0
  55. {microdot-2.3.5 → microdot-2.4.0}/tests/templates/hello.utemplate.txt +0 -0
  56. {microdot-2.3.5 → microdot-2.4.0}/tests/templates/hello_utemplate_txt.py +0 -0
  57. {microdot-2.3.5 → microdot-2.4.0}/tests/test_asgi.py +0 -0
  58. {microdot-2.3.5 → microdot-2.4.0}/tests/test_auth.py +0 -0
  59. {microdot-2.3.5 → microdot-2.4.0}/tests/test_cors.py +0 -0
  60. {microdot-2.3.5 → microdot-2.4.0}/tests/test_end2end.py +0 -0
  61. {microdot-2.3.5 → microdot-2.4.0}/tests/test_jinja.py +0 -0
  62. {microdot-2.3.5 → microdot-2.4.0}/tests/test_login.py +0 -0
  63. {microdot-2.3.5 → microdot-2.4.0}/tests/test_multidict.py +0 -0
  64. {microdot-2.3.5 → microdot-2.4.0}/tests/test_multipart.py +0 -0
  65. {microdot-2.3.5 → microdot-2.4.0}/tests/test_request.py +0 -0
  66. {microdot-2.3.5 → microdot-2.4.0}/tests/test_response.py +0 -0
  67. {microdot-2.3.5 → microdot-2.4.0}/tests/test_session.py +0 -0
  68. {microdot-2.3.5 → microdot-2.4.0}/tests/test_url_pattern.py +0 -0
  69. {microdot-2.3.5 → microdot-2.4.0}/tests/test_urlencode.py +0 -0
  70. {microdot-2.3.5 → microdot-2.4.0}/tests/test_utemplate.py +0 -0
  71. {microdot-2.3.5 → microdot-2.4.0}/tests/test_websocket.py +0 -0
  72. {microdot-2.3.5 → microdot-2.4.0}/tests/test_wsgi.py +0 -0
  73. {microdot-2.3.5 → microdot-2.4.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microdot
3
- Version: 2.3.5
3
+ Version: 2.4.0
4
4
  Summary: The impossibly small web framework for MicroPython
5
5
  Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/miguelgrinberg/microdot
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "microdot"
3
- version = "2.3.5"
3
+ version = "2.4.0"
4
4
  authors = [
5
5
  { name = "Miguel Grinberg", email = "miguel.grinberg@gmail.com" },
6
6
  ]
@@ -1,2 +1,4 @@
1
1
  from microdot.microdot import Microdot, Request, Response, abort, redirect, \
2
2
  send_file, URLPattern, AsyncBytesIO, iscoroutine # noqa: F401
3
+
4
+ __version__ = '2.4.0'
@@ -632,9 +632,13 @@ class Response:
632
632
  """Delete a cookie.
633
633
 
634
634
  :param cookie: The cookie's name.
635
- :param kwargs: Any cookie opens and flags supported by
636
- ``set_cookie()`` except ``expires`` and ``max_age``.
635
+ :param kwargs: Any cookie options and flags supported by
636
+ :meth:`set_cookie() <microdot.Response.set_cookie>`.
637
+ Values given for ``expires`` and ``max_age`` are
638
+ ignored.
637
639
  """
640
+ kwargs.pop('expires', None)
641
+ kwargs.pop('max_age', None)
638
642
  self.set_cookie(cookie, '', expires='Thu, 01 Jan 1970 00:00:01 GMT',
639
643
  max_age=0, **kwargs)
640
644
 
@@ -1367,6 +1371,11 @@ class Microdot:
1367
1371
  try:
1368
1372
  req = await Request.create(self, reader, writer,
1369
1373
  writer.get_extra_info('peername'))
1374
+ except OSError as exc: # pragma: no cover
1375
+ if exc.errno in MUTED_SOCKET_ERRORS:
1376
+ pass
1377
+ else:
1378
+ raise
1370
1379
  except Exception as exc: # pragma: no cover
1371
1380
  print_exception(exc)
1372
1381
 
@@ -25,7 +25,10 @@ class SessionDict(dict):
25
25
  class Session:
26
26
  """
27
27
  :param app: The application instance.
28
- :param key: The secret key, as a string or bytes object.
28
+ :param secret_key: The secret key, as a string or bytes object.
29
+ :param cookie_options: A dictionary with cookie options to pass as
30
+ arguments to :meth:`Response.set_cookie()
31
+ <microdot.Response.set_cookie>`.
29
32
  """
30
33
  secret_key = None
31
34
 
@@ -17,7 +17,8 @@ class SSE:
17
17
  self.event = asyncio.Event()
18
18
  self.queue = []
19
19
 
20
- async def send(self, data, event=None, event_id=None):
20
+ async def send(self, data, event=None, event_id=None, retry=None,
21
+ comment=False):
21
22
  """Send an event to the client.
22
23
 
23
24
  :param data: the data to send. It can be given as a string, bytes, dict
@@ -27,6 +28,12 @@ class SSE:
27
28
  given, it must be a string.
28
29
  :param event_id: an optional event id, to send along with the data. If
29
30
  given, it must be a string.
31
+ :param retry: an optional reconnection time (in seconds) that the
32
+ client should use when the connection is lost.
33
+ :param comment: when set to ``True``, the data is sent as a comment
34
+ line, and all other parameters are ignored. This is
35
+ useful as a heartbeat mechanism that keeps the
36
+ connection alive.
30
37
  """
31
38
  if isinstance(data, (dict, list)):
32
39
  data = json.dumps(data)
@@ -34,11 +41,17 @@ class SSE:
34
41
  data = data.encode()
35
42
  elif not isinstance(data, bytes):
36
43
  data = str(data).encode()
37
- data = b'data: ' + data + b'\n\n'
38
- if event_id:
39
- data = b'id: ' + event_id.encode() + b'\n' + data
40
- if event:
41
- data = b'event: ' + event.encode() + b'\n' + data
44
+ if comment:
45
+ data = b': ' + data + b'\n\n'
46
+ else:
47
+ data = b'data: ' + data + b'\n\n'
48
+ if event_id:
49
+ data = b'id: ' + event_id.encode() + b'\n' + data
50
+ if event:
51
+ data = b'event: ' + event.encode() + b'\n' + data
52
+ if retry:
53
+ data = b'retry: ' + str(int(retry * 1000)).encode() + b'\n' + \
54
+ data
42
55
  self.queue.append(data)
43
56
  self.event.set()
44
57
 
@@ -78,6 +78,7 @@ class TestResponse:
78
78
  data = None
79
79
  event = None
80
80
  event_id = None
81
+ retry = None
81
82
  for line in sse_event.split(b'\n'):
82
83
  if line.startswith(b'data:'):
83
84
  data = line[5:].strip()
@@ -85,6 +86,8 @@ class TestResponse:
85
86
  event = line[6:].strip().decode()
86
87
  elif line.startswith(b'id:'):
87
88
  event_id = line[3:].strip().decode()
89
+ elif line.startswith(b'retry:'):
90
+ retry = int(line[7:].strip()) / 1000
88
91
  if data:
89
92
  data_json = None
90
93
  try:
@@ -92,8 +95,9 @@ class TestResponse:
92
95
  except ValueError:
93
96
  pass
94
97
  self.events.append({
95
- "data": data, "data_json": data_json,
96
- "event": event, "event_id": event_id})
98
+ 'data': data, 'data_json': data_json,
99
+ 'event': event, 'event_id': event_id,
100
+ 'retry': retry})
97
101
 
98
102
  @classmethod
99
103
  async def create(cls, res):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microdot
3
- Version: 2.3.5
3
+ Version: 2.4.0
4
4
  Summary: The impossibly small web framework for MicroPython
5
5
  Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/miguelgrinberg/microdot
@@ -22,7 +22,6 @@ src/microdot/jinja.py
22
22
  src/microdot/login.py
23
23
  src/microdot/microdot.py
24
24
  src/microdot/multipart.py
25
- src/microdot/pubsub.py
26
25
  src/microdot/session.py
27
26
  src/microdot/sse.py
28
27
  src/microdot/test_client.py
@@ -204,9 +204,10 @@ class TestMicrodot(unittest.TestCase):
204
204
  res.set_cookie('four', '4')
205
205
  res.delete_cookie('two', path='/')
206
206
  res.delete_cookie('one', path='/bad')
207
+ res.delete_cookie('five', max_age=123, expires='foo')
207
208
  return res
208
209
 
209
- client = TestClient(app, cookies={'one': '1', 'two': '2'})
210
+ client = TestClient(app, cookies={'one': '1', 'two': '2', 'five': '5'})
210
211
  res = self._run(client.get('/', headers={'Cookie': 'three=3'}))
211
212
  self.assertEqual(res.status_code, 200)
212
213
  self.assertEqual(res.headers['Content-Type'],
@@ -25,7 +25,9 @@ class TestWebSocket(unittest.TestCase):
25
25
  await sse.send('bar', event='test')
26
26
  await sse.send('bar', event='test', event_id='id42')
27
27
  await sse.send('bar', event_id='id42')
28
+ await sse.send('bar', retry=2.5)
28
29
  await sse.send({'foo': 'bar'})
30
+ await sse.send('ping', comment=True)
29
31
  await sse.send([42, 'foo', 'bar'])
30
32
  await sse.send(ValueError('foo'))
31
33
  await sse.send(b'foo')
@@ -38,35 +40,40 @@ class TestWebSocket(unittest.TestCase):
38
40
  'event: test\ndata: bar\n\n'
39
41
  'event: test\nid: id42\ndata: bar\n\n'
40
42
  'id: id42\ndata: bar\n\n'
43
+ 'retry: 2500\ndata: bar\n\n'
41
44
  'data: {"foo": "bar"}\n\n'
45
+ ': ping\n\n'
42
46
  'data: [42, "foo", "bar"]\n\n'
43
47
  'data: foo\n\n'
44
48
  'data: foo\n\n'))
45
- self.assertEqual(len(response.events), 8)
49
+ self.assertEqual(len(response.events), 9)
46
50
  self.assertEqual(response.events[0], {
47
51
  'data': b'foo', 'data_json': None, 'event': None,
48
- 'event_id': None})
52
+ 'event_id': None, "retry": None})
49
53
  self.assertEqual(response.events[1], {
50
54
  'data': b'bar', 'data_json': None, 'event': 'test',
51
- 'event_id': None})
55
+ 'event_id': None, "retry": None})
52
56
  self.assertEqual(response.events[2], {
53
57
  'data': b'bar', 'data_json': None, 'event': 'test',
54
- 'event_id': 'id42'})
58
+ 'event_id': 'id42', "retry": None})
55
59
  self.assertEqual(response.events[3], {
56
60
  'data': b'bar', 'data_json': None, 'event': None,
57
- 'event_id': 'id42'})
61
+ 'event_id': 'id42', "retry": None})
58
62
  self.assertEqual(response.events[4], {
59
- 'data': b'{"foo": "bar"}', 'data_json': {'foo': 'bar'},
60
- 'event': None, 'event_id': None})
63
+ 'data': b'bar', 'data_json': None, 'event': None, 'event_id': None,
64
+ 'retry': 2.5})
61
65
  self.assertEqual(response.events[5], {
62
- 'data': b'[42, "foo", "bar"]', 'data_json': [42, 'foo', 'bar'],
63
- 'event': None, 'event_id': None})
66
+ 'data': b'{"foo": "bar"}', 'data_json': {'foo': 'bar'},
67
+ 'event': None, 'event_id': None, "retry": None})
64
68
  self.assertEqual(response.events[6], {
65
- 'data': b'foo', 'data_json': None, 'event': None,
66
- 'event_id': None})
69
+ 'data': b'[42, "foo", "bar"]', 'data_json': [42, 'foo', 'bar'],
70
+ 'event': None, 'event_id': None, "retry": None})
67
71
  self.assertEqual(response.events[7], {
68
72
  'data': b'foo', 'data_json': None, 'event': None,
69
- 'event_id': None})
73
+ 'event_id': None, "retry": None})
74
+ self.assertEqual(response.events[8], {
75
+ 'data': b'foo', 'data_json': None, 'event': None,
76
+ 'event_id': None, "retry": None})
70
77
 
71
78
  def test_sse_exception(self):
72
79
  app = Microdot()
@@ -1,21 +0,0 @@
1
- class PubSub:
2
- subscriptions = {}
3
-
4
- def subscribe(self, observer, topic):
5
- if topic not in self.subscriptions:
6
- self.subscriptions[topic] = []
7
- self.subscriptions[topic].append(observer)
8
-
9
- def unsubscribe(self, observer, topic):
10
- if topic in self.subscriptions:
11
- try:
12
- self.subscriptions[topic].remove(observer)
13
- if not self.subscriptions[topic]:
14
- del self.subscriptions[topic]
15
- except (ValueError, IndexError):
16
- pass
17
-
18
- @classmethod
19
- async def publish(cls, topic, data):
20
- for observer in cls.subscriptions.get(topic, []):
21
- await observer.send(data, event=topic)
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