Skip to content

Commit e5a9ef5

Browse files
authored
Improve list help and adds list -f=formats. (#113)
Fixes #55
1 parent f44e097 commit e5a9ef5

File tree

3 files changed

+52
-9
lines changed

3 files changed

+52
-9
lines changed

src/manage/commands.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@
8484
]
8585

8686

87-
GLOBAL_OPTIONS_HELP_TEXT = fr"""!G!Global options: !B!(options must come after a command)!W!
87+
GLOBAL_OPTIONS_HELP_TEXT = fr"""!G!Global options: !B!(options must follow the command)!W!
8888
-v, --verbose Increased output (!B!log_level={logging.INFO}!W!)
8989
-vv Further increased output (!B!log_level={logging.DEBUG}!W!)
9090
-q, --quiet Less output (!B!log_level={logging.WARN}!W!)
9191
-qq Even less output (!B!log_level={logging.ERROR}!W!)
92-
-y, --yes Always confirm prompts (!B!confirm=false!W!)
92+
-y, --yes Always accept confirmation prompts (!B!confirm=false!W!)
9393
-h, -?, --help Show help for a specific command
9494
--config=!B!<PATH>!W! Override configuration with JSON file
9595
"""
@@ -665,22 +665,23 @@ class ListCommand(BaseCommand):
665665
> py list !B![options] [<FILTER> ...]!W!
666666
667667
!G!Options:!W!
668-
-f, --format=!B!<table,json,jsonl,id,exe,prefix>!W!
669-
Specify output formatting (!B!list.format=...!W!)
670-
-1, --one Only display first result
668+
-f, --format=!B!<table,json,jsonl,csv,exe,prefix,url,formats>!W!
669+
Specify list format, defaults to !B!table!W!.
670+
Pass !B!-f formats!W! for the full list of formats.
671+
-1, --one Only display first result that matches the filter
671672
--online List runtimes available to install from the default index
672673
-s, --source=!B!<URL>!W!
673674
List runtimes from a particular index
674-
--only-managed Only list Python installs managed by the tool (!B!list.unmanaged=false!W!)
675+
--only-managed Only list Python installs managed by the tool
675676
<FILTER> Filter results (Company\Tag with optional <, <=, >, >= prefix)
676677
677678
!B!EXAMPLE:!W! List all installed runtimes
678679
> py list
679680
680-
!B!EXAMPLE:!W! Display executable of default runtime
681+
!B!EXAMPLE:!W! Display the executable of the default runtime
681682
> py list --one -f=exe
682683
683-
!B!EXAMPLE:!W! Show JSON details for all installs since 3.10
684+
!B!EXAMPLE:!W! Show JSON details for each install since 3.10
684685
> py list -f=jsonl >=3.10
685686
686687
!B!EXAMPLE:!W! Find 3.12 runtimes available for install

src/manage/list_command.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ def _ljust(s, n):
3434

3535

3636
def format_table(cmd, installs):
37+
"Lists as a user-friendly table"
38+
3739
columns = {
3840
"tag-with-co": "Tag",
3941
"default-star": " ",
@@ -143,6 +145,7 @@ def _csv_filter_and_expand(installs, *, exclude=CSV_EXCLUDE, expand=CSV_EXPAND):
143145

144146

145147
def format_csv(cmd, installs):
148+
"List as a comma-separated value table"
146149
import csv
147150
installs = list(_csv_filter_and_expand(installs))
148151
if not installs:
@@ -160,15 +163,18 @@ def write(s):
160163

161164

162165
def format_json(cmd, installs):
166+
"Lists as a single JSON object"
163167
LOGGER.print_raw(json.dumps({"versions": installs}, default=str))
164168

165169

166170
def format_json_lines(cmd, installs):
171+
"Lists as JSON on each line"
167172
for i in installs:
168173
LOGGER.print_raw(json.dumps(i, default=str))
169174

170175

171176
def format_bare_id(cmd, installs):
177+
"Lists the runtime ID"
172178
for i in installs:
173179
# Don't print useless values (__active-virtual-env, __unmanaged-)
174180
if i["id"].startswith("__"):
@@ -177,11 +183,13 @@ def format_bare_id(cmd, installs):
177183

178184

179185
def format_bare_exe(cmd, installs):
186+
"Lists the main executable path"
180187
for i in installs:
181188
LOGGER.print_raw(i["executable"])
182189

183190

184191
def format_bare_prefix(cmd, installs):
192+
"Lists the prefix directory"
185193
for i in installs:
186194
try:
187195
LOGGER.print_raw(i["prefix"])
@@ -190,14 +198,33 @@ def format_bare_prefix(cmd, installs):
190198

191199

192200
def format_bare_url(cmd, installs):
201+
"Lists the original source URL"
193202
for i in installs:
194203
try:
195204
LOGGER.print_raw(i["url"])
196205
except KeyError:
197206
pass
198207

199208

209+
def list_formats(cmd, installs):
210+
"List the available list formats"
211+
max_key_width = len("Format")
212+
items = []
213+
for k, v in FORMATTERS.items():
214+
try:
215+
doc = v.__doc__.partition("\n")[0].strip()
216+
except AttributeError:
217+
doc = ""
218+
if len(k) > max_key_width:
219+
max_key_width = len(k)
220+
items.append((k, doc))
221+
LOGGER.print(f"!B!{'Format':<{max_key_width}} Description!W!", always=True)
222+
for k, doc in items:
223+
LOGGER.print(f"{k:<{max_key_width}} {doc}", always=True)
224+
225+
200226
def format_legacy(cmd, installs, paths=False):
227+
"List runtimes using the old format"
201228
seen_default = False
202229
# TODO: Filter out unmanaged runtimes that have managed equivalents
203230
# The default order (which should be preserved) of 'installs' will put the
@@ -219,6 +246,11 @@ def format_legacy(cmd, installs, paths=False):
219246
LOGGER.print_raw(tag.ljust(17), i["executable"] if paths else i["display-name"])
220247

221248

249+
def format_legacy_paths(cmd, installs):
250+
"List runtime paths using the old format"
251+
return format_legacy(cmd, installs, paths=True)
252+
253+
222254
FORMATTERS = {
223255
"table": format_table,
224256
"csv": format_csv,
@@ -229,7 +261,8 @@ def format_legacy(cmd, installs, paths=False):
229261
"prefix": format_bare_prefix,
230262
"url": format_bare_url,
231263
"legacy": format_legacy,
232-
"legacy-paths": lambda cmd, i: format_legacy(cmd, i, paths=True),
264+
"legacy-paths": format_legacy_paths,
265+
"formats": list_formats,
233266
}
234267

235268

tests/test_list.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,12 @@ def test_csv_expand():
223223
dict(a=5, b=[6]),
224224
dict(a=7, b=8),
225225
]
226+
227+
228+
def test_formats(assert_log):
229+
list_command.list_formats(None, ["fake", "installs", "that", "should", "crash", 123])
230+
assert_log(
231+
r".*Format\s+Description",
232+
r"table\s+Lists.+",
233+
# Assume the rest are okay
234+
)

0 commit comments

Comments
 (0)