Skip to content

Commit 514980c

Browse files
authored
🔀 Merge pull request #48 from davep/cli-ify
Add some CLI functionality
2 parents 5c44b45 + 9fb8737 commit 514980c

File tree

6 files changed

+160
-9
lines changed

6 files changed

+160
-9
lines changed

ChangeLog.md

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

77
- Added a system command for reversing the current sort order.
88
([#46](https://github.com/davep/peplum/pull/46))
9+
- Added `--theme` as a command line switch.
10+
([#48](https://github.com/davep/peplum/pull/48))
11+
- Added `--sort-by` as a command line switch.
12+
([#48](https://github.com/davep/peplum/pull/48))
13+
- Added support for taking a PEP number on the command line and jumping to
14+
it on startup. ([#48](https://github.com/davep/peplum/pull/48))
915

1016
## v0.4.2
1117

src/peplum/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Peplum -- The PEP lookup manager for your terminal."""
1+
"""The PEP lookup manager for your terminal."""
22

33
##############################################################################
44
# Python imports.

src/peplum/__main__.py

+84-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,97 @@
11
"""Main entry point for the application."""
22

3+
##############################################################################
4+
# Python imports.
5+
from argparse import ArgumentParser, Namespace
6+
from inspect import cleandoc
7+
from typing import get_args as get_literal_values
8+
39
##############################################################################
410
# Local imports.
11+
from . import __doc__, __version__
512
from .app import Peplum
13+
from .app.data import SortOrder
14+
15+
16+
##############################################################################
17+
def get_args() -> Namespace:
18+
"""Get the command line arguments.
19+
20+
Returns:
21+
The arguments.
22+
"""
23+
24+
# Build the parser.
25+
parser = ArgumentParser(
26+
prog="peplum",
27+
description=__doc__,
28+
epilog=f"v{__version__}",
29+
)
30+
31+
# Add --version
32+
parser.add_argument(
33+
"-v",
34+
"--version",
35+
help="Show version information",
36+
action="version",
37+
version=f"%(prog)s v{__version__}",
38+
)
39+
40+
# Add --license
41+
parser.add_argument(
42+
"--license",
43+
"--licence",
44+
help="Show license information",
45+
action="store_true",
46+
)
47+
48+
# Add --sort
49+
parser.add_argument(
50+
"-s",
51+
"--sort-by",
52+
help="Set the sort order for the PEPs; prefix with '~' for reverse order",
53+
choices=(
54+
*get_literal_values(SortOrder),
55+
*(f"~{order}" for order in get_literal_values(SortOrder)),
56+
),
57+
)
58+
59+
# Add --theme
60+
parser.add_argument(
61+
"-t",
62+
"--theme",
63+
help="Set the theme for the application (set to ? to list available themes)",
64+
)
65+
66+
# The remainder is going to be the initial command.
67+
parser.add_argument(
68+
"pep",
69+
help="A PEP to highlight",
70+
nargs="?",
71+
)
72+
73+
# Finally, parse the command line.
74+
return parser.parse_args()
75+
76+
77+
##############################################################################
78+
def show_themes() -> None:
79+
"""Show the available themes."""
80+
for theme in sorted(Peplum(Namespace(theme=None)).available_themes):
81+
if theme != "textual-ansi":
82+
print(theme)
683

784

885
##############################################################################
986
def main() -> None:
1087
"""Main entry point."""
11-
Peplum().run()
88+
args = get_args()
89+
if args.license:
90+
print(cleandoc(Peplum.HELP_LICENSE))
91+
elif args.theme == "?":
92+
show_themes()
93+
else:
94+
Peplum(args).run()
1295

1396

1497
##############################################################################

src/peplum/app/data/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
PEPCount,
1818
PEPs,
1919
PythonVersionCount,
20+
SortOrder,
2021
StatusCount,
2122
TypeCount,
2223
WithAuthor,
@@ -44,6 +45,7 @@
4445
"PostHistory",
4546
"PythonVersionCount",
4647
"save_configuration",
48+
"SortOrder",
4749
"StatusCount",
4850
"TypeCount",
4951
"update_configuration",

src/peplum/app/peplum.py

+20-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
"""Provides the main application class."""
22

3+
##############################################################################
4+
# Python imports.
5+
from argparse import Namespace
6+
37
##############################################################################
48
# Textual imports.
59
from textual.app import InvalidThemeError
@@ -48,13 +52,19 @@ class Peplum(EnhancedApp[None]):
4852

4953
COMMANDS = set()
5054

51-
def __init__(self) -> None:
52-
"""Initialise the application."""
55+
def __init__(self, arguments: Namespace) -> None:
56+
"""Initialise the application.
57+
58+
Args:
59+
The command line arguments passed to the application.
60+
"""
61+
self._arguments = arguments
62+
"""The command line arguments passed to the application."""
5363
super().__init__()
5464
configuration = load_configuration()
5565
if configuration.theme is not None:
5666
try:
57-
self.theme = configuration.theme
67+
self.theme = arguments.theme or configuration.theme
5868
except InvalidThemeError:
5969
pass
6070

@@ -63,9 +73,13 @@ def watch_theme(self) -> None:
6373
with update_configuration() as config:
6474
config.theme = self.theme
6575

66-
def on_mount(self) -> None:
67-
"""Display the main screen."""
68-
self.push_screen(Main())
76+
def get_default_screen(self) -> Main:
77+
"""Get the default screen for the application.
78+
79+
Returns:
80+
The main screen.
81+
"""
82+
return Main(self._arguments)
6983

7084

7185
### peplum.py ends here

src/peplum/app/screens/main.py

+47-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
##############################################################################
44
# Python imports.
5+
from argparse import Namespace
56
from dataclasses import dataclass
67
from json import dumps, loads
78
from webbrowser import open as visit_url
@@ -189,6 +190,18 @@ class Main(EnhancedScreen[None]):
189190
notes: var[Notes] = var(Notes)
190191
"""The user's notes about PEPs."""
191192

193+
def __init__(self, arguments: Namespace) -> None:
194+
"""Initialise the main screen.
195+
196+
Args:
197+
arguments: The arguments passed to the application on the command line.
198+
"""
199+
self._arguments = arguments
200+
"""The arguments passed on the command line."""
201+
super().__init__()
202+
self._jump_to_on_load: str | None = self._arguments.pep
203+
"""A PEP to jump to once the display is loaded."""
204+
192205
def compose(self) -> ComposeResult:
193206
"""Compose the content of the main screen."""
194207
yield Header()
@@ -243,9 +256,31 @@ async def download_pep_data(self) -> None:
243256
self.notify("Fresh PEP data downloaded from the PEP API")
244257
self.load_pep_data()
245258

259+
@staticmethod
260+
def _extract_pep(pep: str) -> int | None:
261+
"""Try and extract a PEP number from a string.
262+
263+
Args:
264+
pep: A string that should contain a PEP number.
265+
266+
Returns:
267+
A PEP number or [`None`][None] if one could not be found.
268+
269+
Notes:
270+
The likes of `2342` and `PEP2342` are handled.
271+
"""
272+
try:
273+
return int(pep.strip().upper().removeprefix("PEP"))
274+
except ValueError:
275+
return None
276+
246277
@on(Loaded)
247278
def load_fresh_peps(self, message: Loaded) -> None:
248-
"""React to a fresh set of PEPs being made available."""
279+
"""React to a fresh set of PEPs being made available.
280+
281+
Args:
282+
message: The message letting us know we have fresh PEPs.
283+
"""
249284
if len(message.peps.authors) == 0:
250285
self.notify(
251286
"You likely have a cached copy of the older version of the PEP data; a redownload is recommended.",
@@ -256,9 +291,20 @@ def load_fresh_peps(self, message: Loaded) -> None:
256291
self.all_peps = message.peps.sorted_by(config.peps_sort_order).reversed(
257292
config.peps_sort_reversed
258293
)
294+
if self._jump_to_on_load is not None:
295+
if (pep := self._extract_pep(self._jump_to_on_load)) is not None:
296+
self.post_message(GotoPEP(pep))
297+
self._jump_to_on_load = None
259298

260299
def on_mount(self) -> None:
261300
"""Configure the application once the DOM is mounted."""
301+
# The caller has passed sorting preferences on the command line;
302+
# let's get them into the configuration before anything else kicks
303+
# off.
304+
if self._arguments.sort_by is not None:
305+
with update_configuration() as config:
306+
config.peps_sort_reversed = self._arguments.sort_by[0] == "~"
307+
config.peps_sort_order = self._arguments.sort_by.removeprefix("~")
262308
self.set_class(load_configuration().details_visble, "details-visible")
263309
# On startup, if we've got local PEP data...
264310
if pep_data().exists():

0 commit comments

Comments
 (0)