makememe 0.1.4__tar.gz → 0.1.6__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.4
2
2
  Name: makememe
3
- Version: 0.1.4
3
+ Version: 0.1.6
4
4
  Summary: A tiny zero-dependency CLI for generating memes via the free memegen.link API. Agent-friendly.
5
5
  Project-URL: Homepage, https://pypi.org/project/makememe/
6
6
  Project-URL: memegen.link API, https://api.memegen.link
@@ -67,10 +67,12 @@ meme <template> "top line" "bottom line" [-o out.png]
67
67
 
68
68
  | Flag | Meaning |
69
69
  |------|---------|
70
+ | `-t, --template` | template id as a flag (alternative to the positional). Agents should use this so a single permission approval covers every template |
70
71
  | `-o, --out` | output file (default: a unique file in a temp folder, so it never writes into your current directory) |
71
72
  | `--bg URL` | use a custom background image instead of a template |
72
73
  | `--ext` | `png` (default), `jpg`, `webp`, or `gif` |
73
74
  | `--style` / `--font` | template style variant / font override |
75
+ | `--open` | open the finished image in your default viewer |
74
76
  | `--print-url` | print the image URL, don't download |
75
77
  | `--json` | machine-readable output (for scripts/agents) |
76
78
  | `--list` | list available template ids |
@@ -48,10 +48,12 @@ meme <template> "top line" "bottom line" [-o out.png]
48
48
 
49
49
  | Flag | Meaning |
50
50
  |------|---------|
51
+ | `-t, --template` | template id as a flag (alternative to the positional). Agents should use this so a single permission approval covers every template |
51
52
  | `-o, --out` | output file (default: a unique file in a temp folder, so it never writes into your current directory) |
52
53
  | `--bg URL` | use a custom background image instead of a template |
53
54
  | `--ext` | `png` (default), `jpg`, `webp`, or `gif` |
54
55
  | `--style` / `--font` | template style variant / font override |
56
+ | `--open` | open the finished image in your default viewer |
55
57
  | `--print-url` | print the image URL, don't download |
56
58
  | `--json` | machine-readable output (for scripts/agents) |
57
59
  | `--list` | list available template ids |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "makememe"
7
- version = "0.1.4"
7
+ version = "0.1.6"
8
8
  description = "A tiny zero-dependency CLI for generating memes via the free memegen.link API. Agent-friendly."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -34,11 +34,15 @@ uv tool install makememe
34
34
  **Always verify an id with `meme --list --json` before using it** — guessing
35
35
  ids (e.g. `buttons`, `twobuttons`) leads to 404s. When unsure, list first.
36
36
 
37
- 2. **Generate.** Pass the template id then the caption lines in order. Use
38
- `--json` so you can capture the output path reliably:
37
+ 2. **Generate.** Pass the template id with `-t` (as a flag, not the leading
38
+ word) then the caption lines in order. Always use `--json` (reliable output
39
+ path) and `--open` (opens the finished meme in the user's default viewer).
40
+
41
+ Using `-t` matters: every call starts with the same `meme -t ...` prefix, so
42
+ the user only has to approve the command **once** — not once per template.
39
43
 
40
44
  ```bash
41
- meme drake "writing code by hand" "asking the meme cli" --json
45
+ meme -t drake "writing code by hand" "asking the meme cli" --json --open
42
46
  ```
43
47
 
44
48
  Output:
@@ -47,7 +51,8 @@ uv tool install makememe
47
51
  { "path": "/tmp/makememe/meme-ab12cd.png", "bytes": 12345, "url": "https://api.memegen.link/..." }
48
52
  ```
49
53
 
50
- 3. **Tell the user the path** (and show the image if the surface supports it).
54
+ 3. **Tell the user the path.** The `--open` flag already popped the image open
55
+ for them; just report where it was saved.
51
56
 
52
57
  ## Key flags
53
58
 
@@ -58,6 +63,8 @@ uv tool install makememe
58
63
  - `--bg <image-url>` — use a custom background image instead of a template;
59
64
  pass caption lines as usual.
60
65
  - `--ext png|jpg|webp|gif` — output format.
66
+ - `--open` — open the finished image in the user's default viewer (use this so
67
+ they can see the meme).
61
68
  - `--print-url` — get the image URL without downloading.
62
69
  - `--json` — machine-readable output (always prefer this when scripting).
63
70
 
@@ -77,8 +84,8 @@ uv tool install makememe
77
84
  ## Examples
78
85
 
79
86
  ```bash
80
- meme drake "old way" "new way" -o drake.png --json
81
- meme same "after I sold" "if I held" "same picture" --json
82
- meme cmm "tabs are better than spaces" --json
83
- meme --bg https://example.com/cat.png "_" "DEPLOY ON FRIDAY" --json
87
+ meme -t drake "old way" "new way" --json --open
88
+ meme -t same "after I sold" "if I held" "same picture" --json --open
89
+ meme -t cmm "tabs are better than spaces" --json --open
90
+ meme --bg https://example.com/cat.png "_" "DEPLOY ON FRIDAY" --json --open
84
91
  ```
@@ -22,6 +22,8 @@ Find templates: https://api.memegen.link/templates/ (or `meme --list`)
22
22
  import argparse
23
23
  import json
24
24
  import os
25
+ import shutil
26
+ import subprocess
25
27
  import sys
26
28
  import tempfile
27
29
  import urllib.parse
@@ -30,6 +32,21 @@ import urllib.request
30
32
  API = "https://api.memegen.link"
31
33
 
32
34
 
35
+ def open_file(path):
36
+ """Open a file with the OS default viewer. Best-effort; never raises."""
37
+ try:
38
+ if sys.platform == "darwin":
39
+ subprocess.run(["open", path], check=False)
40
+ elif sys.platform.startswith("win"):
41
+ os.startfile(path) # noqa: B606 (Windows only)
42
+ else:
43
+ opener = shutil.which("xdg-open")
44
+ if opener:
45
+ subprocess.run([opener, path], check=False)
46
+ except Exception:
47
+ pass
48
+
49
+
33
50
  def default_output(ext):
34
51
  """A unique output path in a grouped temp folder, so we never write into the
35
52
  user's project/root dir and never clobber a previous meme."""
@@ -145,6 +162,9 @@ def build_parser():
145
162
  ap.add_argument("template", nargs="?",
146
163
  help="template id (see --list), or any id when using --bg")
147
164
  ap.add_argument("lines", nargs="*", help="text lines, in order")
165
+ ap.add_argument("-t", "--template", dest="template_flag",
166
+ help="template id as a flag (keeps a constant command prefix, "
167
+ "so one permission approval covers every template)")
148
168
  ap.add_argument("-o", "--out", default=None,
149
169
  help="output file (default: a temp folder, not your current dir)")
150
170
  ap.add_argument("--bg",
@@ -152,6 +172,8 @@ def build_parser():
152
172
  ap.add_argument("--ext", default="png", choices=["png", "jpg", "webp", "gif"])
153
173
  ap.add_argument("--style", help="template style variant, if any")
154
174
  ap.add_argument("--font", help="font name (see /fonts/)")
175
+ ap.add_argument("--open", action="store_true", dest="open_after",
176
+ help="open the saved image in the OS default viewer")
155
177
  ap.add_argument("--print-url", action="store_true",
156
178
  help="print the URL and exit, no download")
157
179
  ap.add_argument("--json", action="store_true",
@@ -181,16 +203,20 @@ def _run(argv=None):
181
203
  sys.exit(f"could not fetch templates: {e}")
182
204
  return
183
205
 
184
- if not args.template and not args.bg:
206
+ if not args.template and not args.template_flag and not args.bg:
185
207
  ap.error("a template id is required (or use --bg for a custom background)")
186
208
 
187
- # with --bg there is no template positional, so fold it back into the lines
209
+ # with --bg or -t the template isn't the leading positional, so fold the
210
+ # positional back into the text lines.
188
211
  if args.bg:
189
- lines = ([args.template] if args.template else []) + args.lines
190
212
  template = "custom"
213
+ lines = ([args.template] if args.template else []) + args.lines
214
+ elif args.template_flag:
215
+ template = args.template_flag
216
+ lines = ([args.template] if args.template else []) + args.lines
191
217
  else:
192
- lines = args.lines
193
218
  template = args.template
219
+ lines = args.lines
194
220
 
195
221
  url = build_url(template, lines, args.ext, args.bg, args.style, args.font)
196
222
 
@@ -222,6 +248,9 @@ def _run(argv=None):
222
248
  msg += f"\nhint: {hint}"
223
249
  sys.exit(msg)
224
250
 
251
+ if args.open_after:
252
+ open_file(out)
253
+
225
254
  if args.json:
226
255
  print(json.dumps({"path": out, "bytes": n, "url": url}))
227
256
  else:
@@ -84,6 +84,17 @@ class TestCrashSafety(unittest.TestCase):
84
84
  cli.main(["drake", "a", "b", "--print-url"])
85
85
  self.assertIn("api.memegen.link/images/drake/a/b.png", buf.getvalue())
86
86
 
87
+ def test_template_flag_matches_positional(self):
88
+ def url_for(argv):
89
+ buf = io.StringIO()
90
+ with redirect_stdout(buf):
91
+ cli.main(argv)
92
+ return buf.getvalue().strip()
93
+ self.assertEqual(
94
+ url_for(["-t", "drake", "a", "b", "--print-url"]),
95
+ url_for(["drake", "a", "b", "--print-url"]),
96
+ )
97
+
87
98
  def test_keyboard_interrupt_becomes_130(self):
88
99
  orig = cli.download
89
100
  cli.download = lambda *a, **k: (_ for _ in ()).throw(KeyboardInterrupt())
File without changes
File without changes