pywebpush 2.4.0__tar.gz → 2.5.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.
@@ -1,5 +1,10 @@
1
1
  # I am terrible at keeping this up-to-date.
2
2
 
3
+ ## 2.5.0
4
+
5
+ - Add common `status_code` and `retry_after` accessors to `WebPushException`
6
+ for synchronous and asynchronous responses.
7
+
3
8
  ## 2.3.0 (2026-02-09)
4
9
 
5
10
  - Cleanup from @Rotzbua
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pywebpush
3
- Version: 2.4.0
3
+ Version: 2.5.0
4
4
  Summary: WebPush publication library
5
5
  Author-email: JR Conlin <src+webpusher@jrconlin.com>
6
6
  License-Expression: MPL-2.0
@@ -132,6 +132,11 @@ try:
132
132
  )
133
133
  except WebPushException as ex:
134
134
  print("I'm sorry, Dave, but I can't do that: {}", repr(ex))
135
+ # status_code works with both webpush() and webpush_async().
136
+ if ex.status_code in (429, 503):
137
+ print("Push service requested a retry after:", ex.retry_after)
138
+ # retry_after is either a delay in seconds or an HTTP date:
139
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After
135
140
  # Mozilla returns additional information in the body of the response.
136
141
  if ex.response is not None and ex.response.json():
137
142
  extra = ex.response.json()
@@ -104,6 +104,11 @@ try:
104
104
  )
105
105
  except WebPushException as ex:
106
106
  print("I'm sorry, Dave, but I can't do that: {}", repr(ex))
107
+ # status_code works with both webpush() and webpush_async().
108
+ if ex.status_code in (429, 503):
109
+ print("Push service requested a retry after:", ex.retry_after)
110
+ # retry_after is either a delay in seconds or an HTTP date:
111
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After
107
112
  # Mozilla returns additional information in the body of the response.
108
113
  if ex.response is not None and ex.response.json():
109
114
  extra = ex.response.json()
@@ -219,11 +219,11 @@ If you’re not really into coding your own solution, there’s also a
219
219
 
220
220
  This uses two files:
221
221
 
222
- - the *data* file, which contains the message to send, in whatever form
223
- you like.
224
- - the *subscription info* file, which contains the subscription
225
- information as JSON encoded data. This is usually returned by the Push
226
- ``subscribe`` method and looks something like:
222
+ - the *data* file, which contains the message to send, in whatever form
223
+ you like.
224
+ - the *subscription info* file, which contains the subscription
225
+ information as JSON encoded data. This is usually returned by the
226
+ Push ``subscribe`` method and looks something like:
227
227
 
228
228
  .. code:: json
229
229
 
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
9
9
 
10
10
  [project]
11
11
  name = "pywebpush"
12
- version = "2.4.0"
12
+ version = "2.5.0"
13
13
  # PYTHON_VER
14
14
  requires-python = ">= 3.10"
15
15
  license = "MPL-2.0"
@@ -25,7 +25,11 @@ from requests import Response
25
25
  class WebPushException(Exception):
26
26
  """Web Push failure.
27
27
 
28
- This may contain the requests.Response
28
+ This may contain a requests.Response or aiohttp.ClientResponse.
29
+
30
+ ``status_code`` and ``retry_after`` provide a common interface for
31
+ inspecting either response type without discarding the original
32
+ ``response`` object.
29
33
 
30
34
  """
31
35
 
@@ -44,6 +48,26 @@ class WebPushException(Exception):
44
48
  extra = f", Response {self.response}"
45
49
  return f"WebPushException: {self.message}{extra}"
46
50
 
51
+ @property
52
+ def status_code(self) -> int | None:
53
+ """Return the HTTP status for synchronous or asynchronous responses."""
54
+ if self.response is None:
55
+ return None
56
+ return getattr(
57
+ self.response,
58
+ "status_code",
59
+ getattr(self.response, "status", None),
60
+ )
61
+
62
+ @property
63
+ def retry_after(self) -> str | None:
64
+ """Return the provider's Retry-After header, when present.
65
+
66
+ The value can be either a delay in seconds or an HTTP date. See
67
+ https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After
68
+ """
69
+ return getattr(self.response, "headers", {}).get("Retry-After", None)
70
+
47
71
 
48
72
  class NoData(Exception):
49
73
  """Message contained No Data, no encoding required."""
@@ -563,6 +563,8 @@ class WebpushExceptionTestCase(unittest.TestCase):
563
563
 
564
564
  exp = WebPushException("foo")
565
565
  assert f"{exp}" == "WebPushException: foo"
566
+ assert exp.status_code is None
567
+ assert exp.retry_after is None
566
568
  # Really should try to load the response to verify, but this mock
567
569
  # covers what we need.
568
570
  response = Mock(spec=Response)
@@ -577,9 +579,20 @@ class WebpushExceptionTestCase(unittest.TestCase):
577
579
  response.json.return_value = json.loads(response.text)
578
580
  response.status_code = 401
579
581
  response.reason = "Unauthorized"
582
+ response.headers = {"Retry-After": "120"}
580
583
  exp = WebPushException("foo", response)
581
584
  assert f"{exp}" == "WebPushException: foo, Response {}".format(response.text)
582
585
  assert f"{exp.response}", "<Response [401]>"
583
586
  assert cast(requests.Response, exp.response).json().get("errno") == 109
587
+ assert exp.status_code == 401
588
+ assert exp.retry_after == "120"
589
+
590
+ async_response = Mock(spec=["status", "headers", "text"])
591
+ async_response.status = 503
592
+ async_response.headers = {"Retry-After": "Wed, 21 Oct 2015 07:28:00 GMT"}
593
+ exp = WebPushException("async failure", async_response)
594
+ assert exp.status_code == 503
595
+ assert exp.retry_after == "Wed, 21 Oct 2015 07:28:00 GMT"
596
+
584
597
  exp = WebPushException("foo", [1, 2, 3])
585
598
  assert f"{exp}" == "WebPushException: foo, Response [1, 2, 3]"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pywebpush
3
- Version: 2.4.0
3
+ Version: 2.5.0
4
4
  Summary: WebPush publication library
5
5
  Author-email: JR Conlin <src+webpusher@jrconlin.com>
6
6
  License-Expression: MPL-2.0
@@ -132,6 +132,11 @@ try:
132
132
  )
133
133
  except WebPushException as ex:
134
134
  print("I'm sorry, Dave, but I can't do that: {}", repr(ex))
135
+ # status_code works with both webpush() and webpush_async().
136
+ if ex.status_code in (429, 503):
137
+ print("Push service requested a retry after:", ex.retry_after)
138
+ # retry_after is either a delay in seconds or an HTTP date:
139
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After
135
140
  # Mozilla returns additional information in the body of the response.
136
141
  if ex.response is not None and ex.response.json():
137
142
  extra = ex.response.json()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes