commiefetch 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,2 @@
1
+ __version__ = "1.0.0"
2
+ __app_name__ = "commiefetch"
@@ -0,0 +1,5 @@
1
+ import sys
2
+
3
+ from .cli import main
4
+
5
+ sys.exit(main())
commiefetch/cli.py ADDED
@@ -0,0 +1,394 @@
1
+ import argparse
2
+ import os
3
+ import sys
4
+ import textwrap
5
+ import platform
6
+
7
+ from . import __version__, __app_name__
8
+ from .colors import (
9
+ THEMES, DEFAULT_THEME, apply_theme, colorize, NO_COLOR,
10
+ RESET, BOLD, DIM, ITALIC, BLINK,
11
+ BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE,
12
+ BRIGHT_RED, BRIGHT_GREEN, BRIGHT_YELLOW,
13
+ BRIGHT_BLUE, BRIGHT_MAGENTA, BRIGHT_CYAN, BRIGHT_WHITE,
14
+ BG_RED, BG_GREEN, BG_YELLOW, BG_BLUE, BG_MAGENTA, BG_CYAN, BG_WHITE,
15
+ )
16
+ from .logos import LOGOS, get_logo, list_logos
17
+ from .config import (
18
+ load_config, write_example_config, DEFAULT_MODULES, MODULE_LABELS,
19
+ find_config,
20
+ )
21
+ from .modules import (
22
+ get_user, get_hostname, get_os, get_kernel, get_uptime,
23
+ get_cpu, get_gpu, get_memory, get_swap, get_disk, get_disks,
24
+ get_terminal, get_shell, get_desktop, get_wm, get_packages,
25
+ get_resolution, get_cpu_temp, get_processes, get_local_ip,
26
+ get_public_ip, get_battery, get_locale,
27
+ )
28
+
29
+
30
+ def build_arg_parser():
31
+ parser = argparse.ArgumentParser(
32
+ prog=__app_name__,
33
+ description=f"{__app_name__} — communist-themed system information tool",
34
+ formatter_class=argparse.RawDescriptionHelpFormatter,
35
+ epilog=textwrap.dedent(f"""\
36
+ examples:
37
+ {__app_name__}
38
+ {__app_name__} --logo ussr --theme soviet
39
+ {__app_name__} --logo random
40
+ {__app_name__} --logo prc --theme china
41
+ {__app_name__} --list-logos
42
+ {__app_name__} --list-themes
43
+ {__app_name__} --no-color
44
+ {__app_name__} --config ~/.config/commiefetch/config.json
45
+ {__app_name__} --gen-config
46
+
47
+ docs: https://opencode.ai
48
+ """),
49
+ )
50
+ parser.add_argument(
51
+ "--logo", "-l",
52
+ help="Logo to display (use --list-logos for options, 'random' for random)",
53
+ default=None,
54
+ )
55
+ parser.add_argument(
56
+ "--theme", "-t",
57
+ help=f"Color theme (default: {DEFAULT_THEME})",
58
+ default=None,
59
+ )
60
+ parser.add_argument(
61
+ "--list-logos", action="store_true",
62
+ help="List all available logos",
63
+ )
64
+ parser.add_argument(
65
+ "--list-themes", action="store_true",
66
+ help="List all available color themes",
67
+ )
68
+ parser.add_argument(
69
+ "--no-color", action="store_true",
70
+ help="Disable colored output",
71
+ )
72
+ parser.add_argument(
73
+ "--config", "-c",
74
+ help="Path to config file",
75
+ default=None,
76
+ )
77
+ parser.add_argument(
78
+ "--gen-config", action="store_true",
79
+ help=f"Generate example config at ~/.config/{__app_name__}/config.json",
80
+ )
81
+ parser.add_argument(
82
+ "--modules", "-m",
83
+ nargs="+",
84
+ help="Modules to display (e.g., os kernel cpu memory)",
85
+ default=None,
86
+ )
87
+ parser.add_argument(
88
+ "--version", "-v",
89
+ action="store_true",
90
+ help="Show version and exit",
91
+ )
92
+ parser.add_argument(
93
+ "--separator", "-s",
94
+ help="Separator between label and value",
95
+ default=None,
96
+ )
97
+ parser.add_argument(
98
+ "--padding",
99
+ type=int,
100
+ help="Padding before values",
101
+ default=None,
102
+ )
103
+ parser.add_argument(
104
+ "--show-colors", action="store_true",
105
+ help="Show color palette test",
106
+ )
107
+ parser.add_argument(
108
+ "--title",
109
+ help="Custom title text",
110
+ default=None,
111
+ )
112
+ return parser
113
+
114
+
115
+ def get_module_value(name):
116
+ mappers = {
117
+ "title": lambda: "",
118
+ "os": get_os,
119
+ "host": lambda: f"{get_user()}@{get_hostname()}",
120
+ "kernel": get_kernel,
121
+ "uptime": get_uptime,
122
+ "packages": lambda: str(get_packages()),
123
+ "shell": get_shell,
124
+ "de": get_desktop,
125
+ "wm": get_wm,
126
+ "terminal": get_terminal,
127
+ "cpu": get_cpu,
128
+ "gpu": get_gpu,
129
+ "memory": lambda: _format_memory(get_memory()),
130
+ "disk": lambda: _format_disk(get_disk("/")),
131
+ "disks": lambda: _format_disks(get_disks()),
132
+ "swap": lambda: _format_memory(get_swap()),
133
+ "battery": get_battery,
134
+ "processes": lambda: str(get_processes()),
135
+ "local_ip": get_local_ip,
136
+ "public_ip": get_public_ip,
137
+ "resolution": get_resolution,
138
+ "locale": get_locale,
139
+ "cpu_temp": lambda: "" if get_cpu_temp() == "unknown" else get_cpu_temp(),
140
+ "user_at_host": lambda: f"{get_user()}@{get_hostname()}",
141
+ }
142
+ func = mappers.get(name)
143
+ if func:
144
+ return func()
145
+ return None
146
+
147
+
148
+ def _format_memory(mem):
149
+ if not mem:
150
+ return "unknown"
151
+ if isinstance(mem, dict):
152
+ return f"{mem.get('used', 0)} {mem.get('unit', 'MiB')} / {mem.get('total', 0)} {mem.get('unit', 'MiB')} ({mem.get('percent', 0)}%)"
153
+ return str(mem)
154
+
155
+
156
+ def _format_disk(disk):
157
+ if not disk:
158
+ return "unknown"
159
+ if isinstance(disk, dict):
160
+ total = disk.get('total', 0)
161
+ used = disk.get('used', 0)
162
+ unit = disk.get('unit', 'MiB')
163
+ pct = disk.get('percent', 0)
164
+ if total >= 1024:
165
+ total_g = total / 1024
166
+ used_g = used / 1024
167
+ return f"{used_g:.1f} GiB / {total_g:.1f} GiB ({pct}%)"
168
+ return f"{used} {unit} / {total} {unit} ({pct}%)"
169
+ return str(disk)
170
+
171
+
172
+ def _format_disks(disks):
173
+ if not disks:
174
+ return "unknown"
175
+ parts = []
176
+ for d in disks:
177
+ info = d.get("info", {})
178
+ mount = d.get("mount", "")
179
+ total = info.get("total", 0)
180
+ used = info.get("used", 0)
181
+ pct = info.get("percent", 0)
182
+ if total >= 1024:
183
+ total_g = total / 1024
184
+ used_g = used / 1024
185
+ parts.append(f"{mount}: {used_g:.1f}/{total_g:.1f} GiB ({pct}%)")
186
+ else:
187
+ parts.append(f"{mount}: {used}/{total} MiB ({pct}%)")
188
+ return "\n" + "\n".join(parts)
189
+
190
+
191
+ def color_palette():
192
+ cols = [RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE,
193
+ BRIGHT_RED, BRIGHT_GREEN, BRIGHT_YELLOW,
194
+ BRIGHT_BLUE, BRIGHT_MAGENTA, BRIGHT_CYAN, BRIGHT_WHITE]
195
+ names = ["RED", "GREEN", "YELLOW", "BLUE", "MAGENTA", "CYAN", "WHITE",
196
+ "BRED", "BGRN", "BYEL", "BBLU", "BMAG", "BCYN", "BWHT"]
197
+ result = []
198
+ for i, (c, n) in enumerate(zip(cols, names)):
199
+ block = f"{c}██"
200
+ if i > 0 and i % 7 == 0:
201
+ result.append("\n")
202
+ result.append(f"{block}{RESET} {n:<6} ")
203
+ return "".join(result)
204
+
205
+
206
+ def bg_color_palette():
207
+ bgs = [BG_RED, BG_GREEN, BG_YELLOW, BG_BLUE, BG_MAGENTA, BG_CYAN, BG_WHITE]
208
+ names = ["BG_RED", "BG_GRN", "BG_YLW", "BG_BLU", "BG_MAG", "BG_CYN", "BG_WHT"]
209
+ result = []
210
+ for bg, name in zip(bgs, names):
211
+ block = f"{bg} {RESET}"
212
+ result.append(f"{block} {name:<8} ")
213
+ return "".join(result)
214
+
215
+
216
+ def render_logo(logo_name, theme):
217
+ logo_raw = get_logo(logo_name)
218
+ t = THEMES.get(theme, THEMES[DEFAULT_THEME])
219
+ logo_color = t["logo_color"]
220
+ accent_color = t["accent_color"]
221
+ c = accent_color
222
+
223
+ rendered = logo_raw.format(
224
+ logo=logo_color,
225
+ accent=accent_color,
226
+ c=c,
227
+ )
228
+
229
+ return rendered
230
+
231
+
232
+ def render_info(modules, theme, cfg):
233
+ t = THEMES.get(theme, THEMES[DEFAULT_THEME])
234
+ label_color = t["label_color"]
235
+ value_color = t["value_color"]
236
+ sep_color = t["sep_color"]
237
+ separator = cfg.get("separator", " -> ")
238
+ padding = cfg.get("padding", 2)
239
+ bold_labels = cfg.get("bold_labels", True)
240
+ color_sep = cfg.get("color_separator", True)
241
+
242
+ lines = []
243
+ for mod in modules:
244
+ label = MODULE_LABELS.get(mod, mod.replace("_", " ").title())
245
+ value = get_module_value(mod)
246
+ if value is None or value == "":
247
+ continue
248
+
249
+ if mod == "title":
250
+ user = get_user()
251
+ host = get_hostname()
252
+ title = cfg.get("title")
253
+ if title:
254
+ display_text = title
255
+ else:
256
+ display_text = f"{user}@{host}"
257
+ colored = colorize(display_text, label_color, bold=True)
258
+ lines.append(colored)
259
+ continue
260
+
261
+ if mod == "user_at_host":
262
+ colored = colorize(value, label_color, bold=True)
263
+ lines.append(colored)
264
+ continue
265
+
266
+ sep = separator
267
+ if color_sep:
268
+ sep = f"{sep_color}{separator}{RESET}"
269
+
270
+ pad = " " * padding if padding else " "
271
+ label_str = label
272
+ if bold_labels and not NO_COLOR:
273
+ label_str = f"{BOLD}{label_color}{label}{RESET}"
274
+ elif not NO_COLOR:
275
+ label_str = f"{label_color}{label}{RESET}"
276
+
277
+ val_str = value
278
+ if not NO_COLOR:
279
+ val_str = f"{value_color}{value}{RESET}"
280
+
281
+ lines.append(f"{label_str}{sep}{pad}{val_str}")
282
+
283
+ return lines
284
+
285
+
286
+ def main():
287
+ parser = build_arg_parser()
288
+ args = parser.parse_args()
289
+
290
+ if args.version:
291
+ print(f"{__app_name__} v{__version__}")
292
+ return 0
293
+
294
+ if args.no_color:
295
+ os.environ["NO_COLOR"] = "1"
296
+
297
+ if args.list_logos:
298
+ print(f"{BRIGHT_RED}Available Logos:{RESET}\n")
299
+ logos = list_logos()
300
+ for i, logo_name in enumerate(logos):
301
+ print(f" {BRIGHT_YELLOW}{logo_name}{RESET}")
302
+ return 0
303
+
304
+ if args.list_themes:
305
+ print(f"{BRIGHT_RED}Available Themes:{RESET}\n")
306
+ for name, info in THEMES.items():
307
+ c = info["logo_color"]
308
+ print(f" {c}{name:<12}{RESET} {info['name']}")
309
+ return 0
310
+
311
+ if args.gen_config:
312
+ config_dir = os.path.expanduser(f"~/.config/{__app_name__}")
313
+ config_path = os.path.join(config_dir, "config.json")
314
+ path = write_example_config(config_path)
315
+ print(f"Config written to: {path}")
316
+ return 0
317
+
318
+ if args.show_colors:
319
+ print(f"{BRIGHT_RED}commiefetch Color Palette:{RESET}\n")
320
+ print(color_palette())
321
+ print()
322
+ print(bg_color_palette())
323
+ print()
324
+ return 0
325
+
326
+ cfg = load_config(args.config)
327
+
328
+ # CLI overrides config
329
+ if args.logo:
330
+ cfg["logo"] = args.logo
331
+ if args.theme:
332
+ cfg["theme"] = args.theme
333
+ if args.modules:
334
+ cfg["modules"] = args.modules
335
+ if args.separator:
336
+ cfg["separator"] = args.separator
337
+ if args.padding is not None:
338
+ cfg["padding"] = args.padding
339
+ if args.title:
340
+ cfg["title"] = args.title
341
+
342
+ logo_name = cfg.get("logo", "ussr")
343
+ theme = cfg.get("theme", DEFAULT_THEME)
344
+ modules = cfg.get("modules", DEFAULT_MODULES)
345
+ padding = cfg.get("padding", 2)
346
+
347
+ if logo_name == "random":
348
+ import random
349
+ logo_name = random.choice(list(LOGOS.keys()))
350
+
351
+ logo_art = render_logo(logo_name, theme)
352
+ info_lines = render_info(modules, theme, cfg)
353
+
354
+ logo_lines = logo_art.split("\n")
355
+
356
+ max_logo_width = max(len(line) for line in logo_lines)
357
+
358
+ output_lines = []
359
+ max_lines = max(len(logo_lines), len(info_lines))
360
+
361
+ for i in range(max_lines):
362
+ logo_line = logo_lines[i] if i < len(logo_lines) else ""
363
+ info_line = info_lines[i] if i < len(info_lines) else ""
364
+
365
+ if i == 0 and not info_line:
366
+ output_lines.append(logo_line)
367
+ continue
368
+
369
+ if not info_line and logo_line:
370
+ output_lines.append(logo_line)
371
+ continue
372
+ if not logo_line and info_line:
373
+ pad_left = " " * (max_logo_width + padding)
374
+ output_lines.append(f"{pad_left}{info_line}")
375
+ continue
376
+
377
+ if logo_line:
378
+ pad_to = max_logo_width - len(logo_line) + padding
379
+ if pad_to < 2:
380
+ pad_to = 2
381
+ output_lines.append(f"{logo_line}{' ' * pad_to}{info_line}")
382
+ else:
383
+ output_lines.append(f"{' ' * (max_logo_width + padding)}{info_line}")
384
+
385
+ print()
386
+ for line in output_lines:
387
+ print(line)
388
+ print()
389
+
390
+ return 0
391
+
392
+
393
+ if __name__ == "__main__":
394
+ sys.exit(main())
commiefetch/colors.py ADDED
@@ -0,0 +1,115 @@
1
+ import os
2
+ import sys
3
+
4
+ NO_COLOR = os.environ.get("NO_COLOR") or not sys.stdout.isatty()
5
+
6
+ RESET = "\033[0m" if not NO_COLOR else ""
7
+ BOLD = "\033[1m" if not NO_COLOR else ""
8
+ DIM = "\033[2m" if not NO_COLOR else ""
9
+ ITALIC = "\033[3m" if not NO_COLOR else ""
10
+ BLINK = "\033[5m" if not NO_COLOR else ""
11
+
12
+ BLACK = "\033[30m" if not NO_COLOR else ""
13
+ RED = "\033[31m" if not NO_COLOR else ""
14
+ GREEN = "\033[32m" if not NO_COLOR else ""
15
+ YELLOW = "\033[33m" if not NO_COLOR else ""
16
+ BLUE = "\033[34m" if not NO_COLOR else ""
17
+ MAGENTA = "\033[35m" if not NO_COLOR else ""
18
+ CYAN = "\033[36m" if not NO_COLOR else ""
19
+ WHITE = "\033[37m" if not NO_COLOR else ""
20
+
21
+ BRIGHT_RED = "\033[91m" if not NO_COLOR else ""
22
+ BRIGHT_GREEN = "\033[92m" if not NO_COLOR else ""
23
+ BRIGHT_YELLOW = "\033[93m" if not NO_COLOR else ""
24
+ BRIGHT_BLUE = "\033[94m" if not NO_COLOR else ""
25
+ BRIGHT_MAGENTA = "\033[95m" if not NO_COLOR else ""
26
+ BRIGHT_CYAN = "\033[96m" if not NO_COLOR else ""
27
+ BRIGHT_WHITE = "\033[97m" if not NO_COLOR else ""
28
+
29
+ BG_RED = "\033[41m" if not NO_COLOR else ""
30
+ BG_GREEN = "\033[42m" if not NO_COLOR else ""
31
+ BG_YELLOW = "\033[43m" if not NO_COLOR else ""
32
+ BG_BLUE = "\033[44m" if not NO_COLOR else ""
33
+ BG_MAGENTA = "\033[45m" if not NO_COLOR else ""
34
+ BG_CYAN = "\033[46m" if not NO_COLOR else ""
35
+ BG_WHITE = "\033[47m" if not NO_COLOR else ""
36
+
37
+ THEMES = {
38
+ "soviet": {
39
+ "name": "Soviet Union",
40
+ "logo_color": BRIGHT_RED,
41
+ "label_color": BRIGHT_RED,
42
+ "value_color": BRIGHT_WHITE,
43
+ "accent_color": YELLOW,
44
+ "sep_color": RED,
45
+ },
46
+ "china": {
47
+ "name": "PRC",
48
+ "logo_color": RED,
49
+ "label_color": RED,
50
+ "value_color": BRIGHT_YELLOW,
51
+ "accent_color": YELLOW,
52
+ "sep_color": RED,
53
+ },
54
+ "cuba": {
55
+ "name": "Cuba",
56
+ "logo_color": BRIGHT_BLUE,
57
+ "label_color": RED,
58
+ "value_color": BRIGHT_WHITE,
59
+ "accent_color": BLUE,
60
+ "sep_color": RED,
61
+ },
62
+ "vietnam": {
63
+ "name": "Vietnam",
64
+ "logo_color": YELLOW,
65
+ "label_color": RED,
66
+ "value_color": BRIGHT_YELLOW,
67
+ "accent_color": YELLOW,
68
+ "sep_color": RED,
69
+ },
70
+ "dprk": {
71
+ "name": "DPRK",
72
+ "logo_color": RED,
73
+ "label_color": RED,
74
+ "value_color": BRIGHT_WHITE,
75
+ "accent_color": BRIGHT_BLUE,
76
+ "sep_color": RED,
77
+ },
78
+ "anarcho": {
79
+ "name": "Anarcho-Communism",
80
+ "logo_color": RED,
81
+ "label_color": RED,
82
+ "value_color": BRIGHT_WHITE,
83
+ "accent_color": BLACK,
84
+ "sep_color": RED,
85
+ },
86
+ "minimal": {
87
+ "name": "Minimal",
88
+ "logo_color": WHITE,
89
+ "label_color": WHITE,
90
+ "value_color": WHITE,
91
+ "accent_color": WHITE,
92
+ "sep_color": DIM,
93
+ },
94
+ "retro": {
95
+ "name": "Retro Soviet",
96
+ "logo_color": YELLOW,
97
+ "label_color": BRIGHT_RED,
98
+ "value_color": BRIGHT_YELLOW,
99
+ "accent_color": RED,
100
+ "sep_color": YELLOW,
101
+ },
102
+ }
103
+
104
+ DEFAULT_THEME = "soviet"
105
+
106
+
107
+ def apply_theme(theme_name="soviet"):
108
+ t = THEMES.get(theme_name, THEMES[DEFAULT_THEME])
109
+ return t
110
+
111
+
112
+ def colorize(text, color, bold=False):
113
+ if NO_COLOR:
114
+ return text
115
+ return f"{BOLD if bold else ''}{color}{text}{RESET}"