sygmail 0.1.0__tar.gz → 0.1.1__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,12 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sygmail
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Lightweight Gmail notification wrapper
5
- Author-email: Satoshi Nakabayashi <3104nkb@gmail.com>
5
+ Author: Satoshi Nakabayashi
6
6
  Requires-Python: >=3.9
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
9
  Requires-Dist: yagmail
10
+ Provides-Extra: keyring
11
+ Requires-Dist: keyring; extra == "keyring"
10
12
  Dynamic: license-file
11
13
 
12
14
  # sygmail
@@ -30,6 +32,7 @@ pip install sygmail
30
32
 
31
33
  - Python 3.9+
32
34
  - Dependency: `yagmail`
35
+ - Optional: `keyring` (for OS credential store support)
33
36
 
34
37
  ## Quick start
35
38
 
@@ -53,9 +56,31 @@ SYGMAIL_APP_PASSWORD=app-password
53
56
  SYGMAIL_TO=to@example.com
54
57
  SYGMAIL_SUBJECT=Process Completed
55
58
  SYGMAIL_CONTENTS={script_name} has finished running.
56
- SYGMAIL_ATTACHMENTS_PATH=./a/
59
+ SYGMAIL_ATTACHMENTS_PATH=/path/to/folder
57
60
  ```
58
61
 
62
+ If you use keyring, you can leave `SYGMAIL_APP_PASSWORD` empty.
63
+
64
+ Install keyring support:
65
+
66
+ ```
67
+ pip install "sygmail[keyring]"
68
+ ```
69
+
70
+ Keyring usage:
71
+
72
+ ```bash
73
+ python -m sygmail config set --from you@gmail.com --app-password "app-password" --use-keyring
74
+ python -m sygmail send
75
+ ```
76
+
77
+ Notes:
78
+
79
+ - Passwords are stored under the service name `sygmail` with the `from` address as the username.
80
+ - If `SYGMAIL_APP_PASSWORD` is set, it is used first; otherwise keyring is used.
81
+ - Storage uses your OS credential manager (macOS Keychain, Windows Credential Manager, or a Linux keyring).
82
+ - To remove a stored password, delete it from your OS credential manager.
83
+
59
84
  ## Defaults
60
85
 
61
86
  - Subject: `Process Completed`
@@ -98,8 +123,8 @@ python -m sygmail send \
98
123
  --to to@example.com \
99
124
  --subject "Process Completed" \
100
125
  --contents "[sygmail notification]" \
101
- --attachments ./path/to/file \
102
- --attachments-path ./path/to/folder/
126
+ --attachments /path/to/file \
127
+ --attachments-path /path/to/folder/
103
128
  ```
104
129
 
105
130
  - If `--contents` is omitted, CLI uses `[sygmail notification]` without editing `.env`.
@@ -111,10 +136,14 @@ python -m sygmail send
111
136
 
112
137
  python -m sygmail send --subject "Job Done" --contents "[sygmail notification]"
113
138
 
114
- python -m sygmail send --attachments ./a/a.txt ./a/b.txt
139
+ python -m sygmail send --attachments /path/to/file-a.txt /path/to/file-b.txt
140
+
141
+ python -m sygmail send --attachments-path /path/to/folder/
115
142
 
116
143
  python -m sygmail config set --from you@gmail.com --app-password "app-password"
117
144
 
145
+ python -m sygmail config set --from you@gmail.com --app-password "app-password" --use-keyring
146
+
118
147
  python -m sygmail config show
119
148
  ```
120
149
 
@@ -128,7 +157,7 @@ python -m sygmail config set \
128
157
  --to to@example.com \
129
158
  --subject "Process Completed" \
130
159
  --contents "{script_name} has finished running." \
131
- --attachments-path ./a/
160
+ --attachments-path /path/to/folder/
132
161
 
133
162
  python -m sygmail config reset --env .env
134
163
 
@@ -19,6 +19,7 @@ pip install sygmail
19
19
 
20
20
  - Python 3.9+
21
21
  - Dependency: `yagmail`
22
+ - Optional: `keyring` (for OS credential store support)
22
23
 
23
24
  ## Quick start
24
25
 
@@ -42,9 +43,31 @@ SYGMAIL_APP_PASSWORD=app-password
42
43
  SYGMAIL_TO=to@example.com
43
44
  SYGMAIL_SUBJECT=Process Completed
44
45
  SYGMAIL_CONTENTS={script_name} has finished running.
45
- SYGMAIL_ATTACHMENTS_PATH=./a/
46
+ SYGMAIL_ATTACHMENTS_PATH=/path/to/folder
46
47
  ```
47
48
 
49
+ If you use keyring, you can leave `SYGMAIL_APP_PASSWORD` empty.
50
+
51
+ Install keyring support:
52
+
53
+ ```
54
+ pip install "sygmail[keyring]"
55
+ ```
56
+
57
+ Keyring usage:
58
+
59
+ ```bash
60
+ python -m sygmail config set --from you@gmail.com --app-password "app-password" --use-keyring
61
+ python -m sygmail send
62
+ ```
63
+
64
+ Notes:
65
+
66
+ - Passwords are stored under the service name `sygmail` with the `from` address as the username.
67
+ - If `SYGMAIL_APP_PASSWORD` is set, it is used first; otherwise keyring is used.
68
+ - Storage uses your OS credential manager (macOS Keychain, Windows Credential Manager, or a Linux keyring).
69
+ - To remove a stored password, delete it from your OS credential manager.
70
+
48
71
  ## Defaults
49
72
 
50
73
  - Subject: `Process Completed`
@@ -87,8 +110,8 @@ python -m sygmail send \
87
110
  --to to@example.com \
88
111
  --subject "Process Completed" \
89
112
  --contents "[sygmail notification]" \
90
- --attachments ./path/to/file \
91
- --attachments-path ./path/to/folder/
113
+ --attachments /path/to/file \
114
+ --attachments-path /path/to/folder/
92
115
  ```
93
116
 
94
117
  - If `--contents` is omitted, CLI uses `[sygmail notification]` without editing `.env`.
@@ -100,10 +123,14 @@ python -m sygmail send
100
123
 
101
124
  python -m sygmail send --subject "Job Done" --contents "[sygmail notification]"
102
125
 
103
- python -m sygmail send --attachments ./a/a.txt ./a/b.txt
126
+ python -m sygmail send --attachments /path/to/file-a.txt /path/to/file-b.txt
127
+
128
+ python -m sygmail send --attachments-path /path/to/folder/
104
129
 
105
130
  python -m sygmail config set --from you@gmail.com --app-password "app-password"
106
131
 
132
+ python -m sygmail config set --from you@gmail.com --app-password "app-password" --use-keyring
133
+
107
134
  python -m sygmail config show
108
135
  ```
109
136
 
@@ -117,7 +144,7 @@ python -m sygmail config set \
117
144
  --to to@example.com \
118
145
  --subject "Process Completed" \
119
146
  --contents "{script_name} has finished running." \
120
- --attachments-path ./a/
147
+ --attachments-path /path/to/folder/
121
148
 
122
149
  python -m sygmail config reset --env .env
123
150
 
@@ -167,4 +194,4 @@ Sygmail.send(
167
194
  ## Operations
168
195
 
169
196
  - Prefer `chmod 600 .env` on shared machines.
170
- - Use `--env` to separate configs per project.
197
+ - Use `--env` to separate configs per project.
@@ -4,12 +4,15 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sygmail"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  description = "Lightweight Gmail notification wrapper"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
11
11
  dependencies = ["yagmail"]
12
- authors = [{name = "Satoshi Nakabayashi", email = "3104nkb@gmail.com"}]
12
+ authors = [{name = "Satoshi Nakabayashi"}]
13
+
14
+ [project.optional-dependencies]
15
+ keyring = ["keyring"]
13
16
 
14
17
  [project.scripts]
15
18
  sygmail = "sygmail.cli:main"
@@ -36,6 +36,11 @@ def build_parser() -> argparse.ArgumentParser:
36
36
  config_set_parser.add_argument("--env", default=".env", help="Path to .env file")
37
37
  config_set_parser.add_argument("--from", dest="from_addr", help="From address")
38
38
  config_set_parser.add_argument("--app-password", help="Gmail app password")
39
+ config_set_parser.add_argument(
40
+ "--use-keyring",
41
+ action="store_true",
42
+ help="Store app password in keyring instead of .env",
43
+ )
39
44
  config_set_parser.add_argument("--to", help="To address")
40
45
  config_set_parser.add_argument("--subject", help="Email subject")
41
46
  config_set_parser.add_argument("--contents", help="Email contents")
@@ -110,6 +115,7 @@ def run_config_set(args: argparse.Namespace) -> int:
110
115
  subject=args.subject,
111
116
  contents=args.contents,
112
117
  attachments_path=args.attachments_path,
118
+ use_keyring=args.use_keyring,
113
119
  persist=True,
114
120
  )
115
121
  return 0
@@ -7,8 +7,14 @@ from typing import Iterable, List, Optional
7
7
 
8
8
  import yagmail
9
9
 
10
+ try:
11
+ import keyring
12
+ except ImportError: # pragma: no cover - optional dependency
13
+ keyring = None
14
+
10
15
  DEFAULT_SUBJECT = "Process Completed"
11
16
  DEFAULT_CONTENTS_TEMPLATE = "{script_name} has finished running."
17
+ KEYRING_SERVICE = "sygmail"
12
18
 
13
19
  ENV_KEYS = {
14
20
  "from_addr": "SYGMAIL_FROM",
@@ -80,13 +86,20 @@ class Sygmail:
80
86
  subject: Optional[str] = None,
81
87
  contents: Optional[str] = None,
82
88
  attachments_path: Optional[str] = None,
89
+ use_keyring: bool = False,
83
90
  persist: bool = True,
84
91
  ) -> None:
85
92
  if from_addr is None and from_ is not None:
86
93
  from_addr = from_
87
94
  if from_addr is not None:
88
95
  self.config.from_addr = from_addr
89
- if app_password is not None:
96
+ if app_password is not None and use_keyring:
97
+ resolved_from = from_addr or self.config.from_addr
98
+ if not resolved_from:
99
+ raise ValueError("from_addr is required to store keyring password")
100
+ _store_keyring_password(resolved_from, app_password)
101
+ self.config.app_password = None
102
+ elif app_password is not None:
90
103
  self.config.app_password = app_password
91
104
  if to is not None:
92
105
  self.config.to = to
@@ -117,7 +130,7 @@ class Sygmail:
117
130
  **kwargs,
118
131
  ) -> None:
119
132
  resolved_from = from_addr or from_ or self.config.from_addr
120
- app_password = self.config.app_password
133
+ app_password = self.config.app_password or _get_keyring_password(resolved_from)
121
134
  target = to or self.config.to or resolved_from
122
135
 
123
136
  if not resolved_from or not app_password or not target:
@@ -205,6 +218,21 @@ def _warn_missing_attachments(paths: Iterable[str]) -> None:
205
218
  warnings.warn(f"missing attachments ignored: {joined}", stacklevel=3)
206
219
 
207
220
 
221
+ def _get_keyring_password(username: Optional[str]) -> Optional[str]:
222
+ if not username or keyring is None:
223
+ return None
224
+ try:
225
+ return keyring.get_password(KEYRING_SERVICE, username)
226
+ except Exception:
227
+ return None
228
+
229
+
230
+ def _store_keyring_password(username: str, password: str) -> None:
231
+ if keyring is None:
232
+ raise RuntimeError("keyring is not installed")
233
+ keyring.set_password(KEYRING_SERVICE, username, password)
234
+
235
+
208
236
  def _read_env_file(env_path: str) -> dict:
209
237
  path = Path(env_path)
210
238
  if not path.exists():
@@ -1,12 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sygmail
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Lightweight Gmail notification wrapper
5
- Author-email: Satoshi Nakabayashi <3104nkb@gmail.com>
5
+ Author: Satoshi Nakabayashi
6
6
  Requires-Python: >=3.9
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
9
  Requires-Dist: yagmail
10
+ Provides-Extra: keyring
11
+ Requires-Dist: keyring; extra == "keyring"
10
12
  Dynamic: license-file
11
13
 
12
14
  # sygmail
@@ -30,6 +32,7 @@ pip install sygmail
30
32
 
31
33
  - Python 3.9+
32
34
  - Dependency: `yagmail`
35
+ - Optional: `keyring` (for OS credential store support)
33
36
 
34
37
  ## Quick start
35
38
 
@@ -53,9 +56,31 @@ SYGMAIL_APP_PASSWORD=app-password
53
56
  SYGMAIL_TO=to@example.com
54
57
  SYGMAIL_SUBJECT=Process Completed
55
58
  SYGMAIL_CONTENTS={script_name} has finished running.
56
- SYGMAIL_ATTACHMENTS_PATH=./a/
59
+ SYGMAIL_ATTACHMENTS_PATH=/path/to/folder
57
60
  ```
58
61
 
62
+ If you use keyring, you can leave `SYGMAIL_APP_PASSWORD` empty.
63
+
64
+ Install keyring support:
65
+
66
+ ```
67
+ pip install "sygmail[keyring]"
68
+ ```
69
+
70
+ Keyring usage:
71
+
72
+ ```bash
73
+ python -m sygmail config set --from you@gmail.com --app-password "app-password" --use-keyring
74
+ python -m sygmail send
75
+ ```
76
+
77
+ Notes:
78
+
79
+ - Passwords are stored under the service name `sygmail` with the `from` address as the username.
80
+ - If `SYGMAIL_APP_PASSWORD` is set, it is used first; otherwise keyring is used.
81
+ - Storage uses your OS credential manager (macOS Keychain, Windows Credential Manager, or a Linux keyring).
82
+ - To remove a stored password, delete it from your OS credential manager.
83
+
59
84
  ## Defaults
60
85
 
61
86
  - Subject: `Process Completed`
@@ -98,8 +123,8 @@ python -m sygmail send \
98
123
  --to to@example.com \
99
124
  --subject "Process Completed" \
100
125
  --contents "[sygmail notification]" \
101
- --attachments ./path/to/file \
102
- --attachments-path ./path/to/folder/
126
+ --attachments /path/to/file \
127
+ --attachments-path /path/to/folder/
103
128
  ```
104
129
 
105
130
  - If `--contents` is omitted, CLI uses `[sygmail notification]` without editing `.env`.
@@ -111,10 +136,14 @@ python -m sygmail send
111
136
 
112
137
  python -m sygmail send --subject "Job Done" --contents "[sygmail notification]"
113
138
 
114
- python -m sygmail send --attachments ./a/a.txt ./a/b.txt
139
+ python -m sygmail send --attachments /path/to/file-a.txt /path/to/file-b.txt
140
+
141
+ python -m sygmail send --attachments-path /path/to/folder/
115
142
 
116
143
  python -m sygmail config set --from you@gmail.com --app-password "app-password"
117
144
 
145
+ python -m sygmail config set --from you@gmail.com --app-password "app-password" --use-keyring
146
+
118
147
  python -m sygmail config show
119
148
  ```
120
149
 
@@ -128,7 +157,7 @@ python -m sygmail config set \
128
157
  --to to@example.com \
129
158
  --subject "Process Completed" \
130
159
  --contents "{script_name} has finished running." \
131
- --attachments-path ./a/
160
+ --attachments-path /path/to/folder/
132
161
 
133
162
  python -m sygmail config reset --env .env
134
163
 
@@ -0,0 +1,4 @@
1
+ yagmail
2
+
3
+ [keyring]
4
+ keyring
@@ -1 +0,0 @@
1
- yagmail
File without changes
File without changes
File without changes
File without changes