python-ntfy 0.2.3__tar.gz → 0.2.5__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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-ntfy
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: An ntfy library aiming for feature completeness
5
5
  Home-page: https://github.com/MatthewCane/python-ntfy
6
6
  License: MIT
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
16
  Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Dist: mypy (>=1.12.0,<2.0.0)
17
18
  Requires-Dist: requests (>=2.31.0,<3.0.0)
18
19
  Project-URL: Documentation, https://matthewcane.github.io/python-ntfy/
19
20
  Project-URL: Repository, https://github.com/MatthewCane/python-ntfy
@@ -62,21 +63,25 @@ See the full documentation site at [https://matthewcane.github.io/python-ntfy/](
62
63
  - Email notifications
63
64
  - Send to multiple topics at once
64
65
 
65
- ## Test and Development
66
+ ## Testing and Development
66
67
 
67
68
  This project uses:
68
69
 
69
70
  - Poetry as it's dependency manager
70
71
  - Ruff for linting and code formatting
72
+ - MyPy for static type checking
71
73
 
72
74
  To install dev dependencies, run `poetry install --with dev`.
73
75
 
74
- ### Linting and Formatting
76
+ ### Linting, Formatting and Type Checking
75
77
 
76
78
  These can be run with:
77
79
 
78
80
  - `poetry run ruff format`
79
81
  - `poetry run ruff check`
82
+ - `poetry run mypy .`
83
+
84
+ These tools are also run in the CI pipeline and must pass before merging.
80
85
 
81
86
  ### Tests
82
87
 
@@ -87,11 +92,11 @@ You can run the tests against a local instance of `ntfy` *or* `ntfy.sh`.
87
92
  #### Setup Steps
88
93
 
89
94
  1. To test against a *local* `ntfy` instance:
90
- i. Create a container using `docker run -p 80:80 -it binwiederhier/ntfy serve --attachment-cache-dir=/cache --base-url=http://localhost`
91
- ii. Set the following key in the `.env` file: `NTFY_SERVER=http://localhost`
92
- iii. Add a dummy username and password to `.env` (see example.env)
93
- 2. To test against https://ntfy.sh:
94
- i. Add username and password for ntfy.sh to `.env` (see example.env)
95
+ i. Create a container using `docker run -p 80:80 -it binwiederhier/ntfy serve --attachment-cache-dir=/cache --base-url=http://localhost`
96
+ ii. Set the following key in the `.env` file: `NTFY_SERVER=http://localhost`
97
+ iii. Add a dummy username and password to `.env` (see example.env)
98
+ 2. To test against `https://ntfy.sh`:
99
+ i. Add username and password for ntfy.sh to `.env` (see example.env)
95
100
  3. Run the tests with `poetry run pytest --cov`
96
101
 
97
102
  The tests will sent messages to the `python_ntfy_testing` topic so you will need to subcribe to that topic to see the test messages.
@@ -41,21 +41,25 @@ See the full documentation site at [https://matthewcane.github.io/python-ntfy/](
41
41
  - Email notifications
42
42
  - Send to multiple topics at once
43
43
 
44
- ## Test and Development
44
+ ## Testing and Development
45
45
 
46
46
  This project uses:
47
47
 
48
48
  - Poetry as it's dependency manager
49
49
  - Ruff for linting and code formatting
50
+ - MyPy for static type checking
50
51
 
51
52
  To install dev dependencies, run `poetry install --with dev`.
52
53
 
53
- ### Linting and Formatting
54
+ ### Linting, Formatting and Type Checking
54
55
 
55
56
  These can be run with:
56
57
 
57
58
  - `poetry run ruff format`
58
59
  - `poetry run ruff check`
60
+ - `poetry run mypy .`
61
+
62
+ These tools are also run in the CI pipeline and must pass before merging.
59
63
 
60
64
  ### Tests
61
65
 
@@ -66,11 +70,11 @@ You can run the tests against a local instance of `ntfy` *or* `ntfy.sh`.
66
70
  #### Setup Steps
67
71
 
68
72
  1. To test against a *local* `ntfy` instance:
69
- i. Create a container using `docker run -p 80:80 -it binwiederhier/ntfy serve --attachment-cache-dir=/cache --base-url=http://localhost`
70
- ii. Set the following key in the `.env` file: `NTFY_SERVER=http://localhost`
71
- iii. Add a dummy username and password to `.env` (see example.env)
72
- 2. To test against https://ntfy.sh:
73
- i. Add username and password for ntfy.sh to `.env` (see example.env)
73
+ i. Create a container using `docker run -p 80:80 -it binwiederhier/ntfy serve --attachment-cache-dir=/cache --base-url=http://localhost`
74
+ ii. Set the following key in the `.env` file: `NTFY_SERVER=http://localhost`
75
+ iii. Add a dummy username and password to `.env` (see example.env)
76
+ 2. To test against `https://ntfy.sh`:
77
+ i. Add username and password for ntfy.sh to `.env` (see example.env)
74
78
  3. Run the tests with `poetry run pytest --cov`
75
79
 
76
80
  The tests will sent messages to the `python_ntfy_testing` topic so you will need to subcribe to that topic to see the test messages.
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-ntfy"
3
- version = "0.2.3"
3
+ version = "0.2.5"
4
4
  description = "An ntfy library aiming for feature completeness"
5
5
  authors = ["Matthew Cane <matthew.cane0@gmail.com>"]
6
6
  readme = "README.md"
@@ -15,6 +15,7 @@ classifiers = [
15
15
  [tool.poetry.dependencies]
16
16
  python = "^3.11"
17
17
  requests = "^2.31.0"
18
+ mypy = "^1.12.0"
18
19
 
19
20
  [tool.poetry.group.dev.dependencies]
20
21
  pytest = ">=7.4.1,<9.0.0"
@@ -24,6 +25,10 @@ pytest-codecov = ">=0.5.1,<0.7.0"
24
25
  ruff = "^0.7.0"
25
26
  mkdocs-material = "^9.5.41"
26
27
  mkdocstrings-python = "^1.12.1"
28
+ types-pygments = "^2.18.0.20240506"
29
+ types-colorama = "^0.4.15.20240311"
30
+ types-requests = "^2.32.0.20241016"
31
+ types-setuptools = "^75.2.0.20241018"
27
32
 
28
33
  [build-system]
29
34
  requires = ["poetry-core"]
@@ -2,7 +2,12 @@ import os
2
2
 
3
3
 
4
4
  class NtfyClient:
5
- from ._send_functions import (
5
+ # The functions need to be imported here to:
6
+ # 1. Keep the functions in a separate file
7
+ # 2. Keep the docstrings working in the IDE
8
+ # 3. Allow the functions to be called with self
9
+ # MyPy does not like this, but it works
10
+ from ._send_functions import ( # type: ignore
6
11
  send,
7
12
  send_file,
8
13
  MessagePriority,
@@ -10,18 +15,19 @@ class NtfyClient:
10
15
  BroadcastAction,
11
16
  HttpAction,
12
17
  )
13
- from ._get_functions import get_cached_messages
18
+ from ._get_functions import get_cached_messages # type: ignore
14
19
 
15
20
  def __init__(
16
21
  self,
17
22
  topic: str,
18
23
  server: str = "https://ntfy.sh",
19
- ):
24
+ ) -> None:
20
25
  """
21
26
  :param topic: The topic to use for this client
22
27
  :param server: The server to connect to. Must include the protocol (http/https)
23
- :return:
28
+ :return None:
24
29
  """
30
+
25
31
  self._server = os.environ.get("NTFY_SERVER") or server
26
32
  self._topic = topic
27
33
  self.__set_url(self._server, topic)
@@ -45,3 +51,11 @@ class NtfyClient:
45
51
  """
46
52
  self._topic = topic
47
53
  self.__set_url(self._server, self._topic)
54
+
55
+ def get_topic(self):
56
+ """
57
+ Get the current topic
58
+
59
+ :return: str
60
+ """
61
+ return self._topic
@@ -21,7 +21,7 @@ def get_cached_messages(
21
21
 
22
22
  params = {"poll": "1"}
23
23
  if scheduled:
24
- params.update({"scheduled": scheduled})
24
+ params.update({"scheduled": str(scheduled)})
25
25
  if since:
26
26
  params.update({"since": since})
27
27
 
@@ -57,13 +57,13 @@ class BroadcastAction(Action):
57
57
  self,
58
58
  label: str,
59
59
  intent: str = "io.heckel.ntfy.USER_ACTION",
60
- extras: Optional[dict] = None,
60
+ extras: Optional[dict[str, str]] = None,
61
61
  clear: bool = False,
62
62
  ):
63
63
  self.action = ActionType.BROADCAST
64
64
  self.intent = intent
65
65
  self.extras = extras
66
- super().__init__(label, ActionType.BROADCAST, clear)
66
+ super().__init__(label, ActionType.BROADCAST.value, clear)
67
67
 
68
68
  def to_dict(self) -> dict:
69
69
  return {
@@ -71,14 +71,18 @@ class BroadcastAction(Action):
71
71
  "label": self.label,
72
72
  "extras": self.extras,
73
73
  "clear": self.clear,
74
+ "intent": self.intent,
74
75
  }
75
76
 
76
77
  def to_header(self) -> str:
77
- extras = ""
78
- if self.extras is not None:
79
- for key, value in self.extras.items():
80
- extras += f"{key}={value},"
81
- return f"action={self.action.value}, label={self.label}, url={self.url}, clear={self.clear}"
78
+ if self.extras:
79
+ extras_str = ", ".join([f"extras.{k}={v}" for k, v in self.extras.items()])
80
+ return (
81
+ f"action={self.action.value}, label={self.label}, url={self.url}, clear={self.clear}, intent={self.intent}"
82
+ + extras_str
83
+ if self.extras
84
+ else ""
85
+ )
82
86
 
83
87
 
84
88
  class HttpAction(Action):
@@ -87,7 +91,7 @@ class HttpAction(Action):
87
91
  label: str,
88
92
  url: str,
89
93
  method: str = "POST",
90
- headers: Optional[dict] = None,
94
+ headers: Optional[dict[str, str]] = None,
91
95
  body: Optional[str] = None,
92
96
  clear: bool = False,
93
97
  ):
@@ -97,8 +101,8 @@ class HttpAction(Action):
97
101
  self.body = body
98
102
  super().__init__(label, url, clear)
99
103
 
100
- def to_dict(self) -> dict:
101
- action_dict = {
104
+ def to_dict(self) -> dict[str, Union[str, bool, dict[str, str]]]:
105
+ action_dict: dict[str, Union[str, bool, dict[str, str]]] = {
102
106
  "action": self.action.value,
103
107
  "label": self.label,
104
108
  "url": self.url,
@@ -127,10 +131,10 @@ class HttpAction(Action):
127
131
  def send(
128
132
  self,
129
133
  message: str,
130
- title: str = None,
131
- priority: Optional[MessagePriority] = MessagePriority.DEFAULT,
134
+ title: Optional[str] = None,
135
+ priority: MessagePriority = MessagePriority.DEFAULT,
132
136
  tags: list = [],
133
- actions: list[Union[ViewAction, BroadcastAction, HttpAction, None]] = [],
137
+ actions: list[Union[ViewAction, BroadcastAction, HttpAction]] = [],
134
138
  format_as_markdown: bool = False,
135
139
  ) -> dict:
136
140
  """
@@ -153,7 +157,7 @@ def send(
153
157
  "Title": title,
154
158
  "Priority": priority.value,
155
159
  "Tags": ",".join(tags),
156
- "Markdown": "true" if format_as_markdown else "false",
160
+ "Markdown": str(format_as_markdown).lower(),
157
161
  }
158
162
  if len(actions) > 0:
159
163
  headers["Actions"] = " ; ".join([action.to_header() for action in actions])
@@ -167,10 +171,10 @@ def send(
167
171
  def send_file(
168
172
  self,
169
173
  file: str,
170
- title: str = None,
171
- priority: Optional[MessagePriority] = MessagePriority.DEFAULT,
174
+ title: Optional[str] = None,
175
+ priority: MessagePriority = MessagePriority.DEFAULT,
172
176
  tags: list = [],
173
- actions: list[Union[ViewAction, BroadcastAction, HttpAction, None]] = [],
177
+ actions: list[Union[ViewAction, BroadcastAction, HttpAction]] = [],
174
178
  ) -> dict:
175
179
  """
176
180
  Send a file to the server
@@ -186,17 +190,15 @@ def send_file(
186
190
  response = client.send_file(file_path="example.txt")
187
191
  """
188
192
  headers = {
189
- "Title": title,
193
+ "Title": str(title),
190
194
  "Filename": file.split("/")[-1],
191
195
  "Priority": priority.value,
192
196
  "Tags": ",".join(tags),
193
197
  "Actions": " ; ".join([action.to_header() for action in actions]),
194
198
  }
195
199
 
196
- with open(file, "rb") as file:
200
+ with open(file, "rb") as f:
197
201
  response = json.loads(
198
- requests.post(
199
- url=self.url, data=file, headers=headers, auth=self._auth
200
- ).text
202
+ requests.post(url=self.url, data=f, headers=headers, auth=self._auth).text
201
203
  )
202
204
  return response
File without changes