9
9
"RichHelpFormatter" ,
10
10
"IndentedRichHelpFormatter" ,
11
11
"TitledRichHelpFormatter" ,
12
+ "GENERATE_USAGE" ,
12
13
]
13
14
15
+ GENERATE_USAGE = "==GENERATE_USAGE=="
16
+
14
17
15
18
class RichHelpFormatter (optparse .HelpFormatter ):
16
19
"""An optparse HelpFormatter class that renders using rich."""
@@ -22,6 +25,7 @@ class RichHelpFormatter(optparse.HelpFormatter):
22
25
"optparse.metavar" : "dark_cyan" ,
23
26
"optparse.syntax" : "bold" ,
24
27
"optparse.text" : "default" ,
28
+ "optparse.prog" : "grey50" ,
25
29
}
26
30
"""A dict of rich styles to control the formatter styles.
27
31
@@ -31,6 +35,7 @@ class RichHelpFormatter(optparse.HelpFormatter):
31
35
- ``optparse.groups``: for group names (e.g. "Options")
32
36
- ``optparse.help``: for options's help text (e.g. "show this help message and exit")
33
37
- ``optparse.metavar``: for meta variables (e.g. "FILE" in "--file=FILE")
38
+ - ``argparse.prog``: for %prog in generated usage (e.g. "foo" in "Usage: foo [options]")
34
39
- ``optparse.syntax``: for highlights of back-tick quoted text (e.g. "``` `some text` ```"),
35
40
- ``optparse.text``: for the descriptions and epilog (e.g. "A foo program")
36
41
"""
@@ -75,6 +80,12 @@ def _stringify(self, text: r.RenderableType) -> str:
75
80
help = _fix_legacy_win_text (self .console , help )
76
81
return help
77
82
83
+ def rich_format_usage (self , usage : str ) -> r .Text :
84
+ raise NotImplementedError ("subclasses must implement" )
85
+
86
+ def rich_format_heading (self , heading : str ) -> r .Text :
87
+ raise NotImplementedError ("subclasses must implement" )
88
+
78
89
def _rich_format_text (self , text : str ) -> r .Text :
79
90
# HelpFormatter._format_text() equivalent that produces rich.text.Text
80
91
text_width = max (self .width - 2 * self .current_indent , 11 )
@@ -84,15 +95,31 @@ def _rich_format_text(self, text: str) -> r.Text:
84
95
rich_text .highlight_regex (highlight , style_prefix = "optparse." )
85
96
return _rich_fill (self .console , rich_text , text_width , indent )
86
97
87
- def format_description (self , description : str ) -> str :
98
+ def rich_format_description (self , description : str ) -> r . Text :
88
99
if not description :
89
- return ""
90
- return self ._stringify ( self . _rich_format_text (description )) + "\n "
100
+ return r . Text ()
101
+ return self ._rich_format_text (description ) + r . Text ( "\n " )
91
102
92
- def format_epilog (self , epilog : str ) -> str :
103
+ def rich_format_epilog (self , epilog : str ) -> r . Text :
93
104
if not epilog :
94
- return ""
95
- return "\n " + self ._stringify (self ._rich_format_text (epilog )) + "\n "
105
+ return r .Text ()
106
+ return r .Text ("\n " ) + self ._rich_format_text (epilog ) + r .Text ("\n " )
107
+
108
+ def format_usage (self , usage : str ) -> str :
109
+ if usage is GENERATE_USAGE :
110
+ rich_usage = self ._generate_usage ()
111
+ else :
112
+ rich_usage = self .rich_format_usage (usage )
113
+ return self ._stringify (rich_usage )
114
+
115
+ def format_heading (self , heading : str ) -> str :
116
+ return self ._stringify (self .rich_format_heading (heading ))
117
+
118
+ def format_description (self , description : str ) -> str :
119
+ return self ._stringify (self .rich_format_description (description ))
120
+
121
+ def format_epilog (self , epilog : str ) -> str :
122
+ return self ._stringify (self .rich_format_epilog (epilog ))
96
123
97
124
def rich_expand_default (self , option : optparse .Option ) -> r .Text :
98
125
assert option .help is not None
@@ -108,7 +135,7 @@ def rich_expand_default(self, option: optparse.Option) -> r.Text:
108
135
rich_help .highlight_regex (highlight , style_prefix = "optparse." )
109
136
return rich_help
110
137
111
- def format_option (self , option : optparse .Option ) -> str :
138
+ def rich_format_option (self , option : optparse .Option ) -> r . Text :
112
139
result : list [r .Text ] = []
113
140
opts = self .rich_option_strings [option ]
114
141
opt_width = self .help_position - self .current_indent - 2
@@ -131,7 +158,10 @@ def format_option(self, option: optparse.Option) -> str:
131
158
result .append (r .Text ("\n " ))
132
159
else :
133
160
pass # pragma: no cover
134
- return self ._stringify (r .Text ().join (result ))
161
+ return r .Text ().join (result )
162
+
163
+ def format_option (self , option : optparse .Option ) -> str :
164
+ return self ._stringify (self .rich_format_option (option ))
135
165
136
166
def store_option_strings (self , parser : optparse .OptionParser ) -> None :
137
167
self .indent ()
@@ -185,8 +215,34 @@ def rich_format_option_strings(self, option: optparse.Option) -> r.Text:
185
215
186
216
return r .Text (", " ).join (opts )
187
217
218
+ def _generate_usage (self ) -> r .Text :
219
+ """Generate usage string from the parser's actions."""
220
+ if self .parser is None :
221
+ raise TypeError ("Cannot generate usage if parser is not set" )
222
+ mark = "==GENERATED_USAGE_MARKER=="
223
+ usage_lines : list [r .Text ] = []
224
+ prefix = self .rich_format_usage (mark ).split (mark )[0 ]
225
+ usage_lines .extend (prefix .split ("\n " ))
226
+ usage_lines [- 1 ].append (self .parser .get_prog_name (), "optparse.prog" )
227
+ indent = len (usage_lines [- 1 ]) + 1
228
+ for option in self .parser .option_list :
229
+ if option .help == optparse .SUPPRESS_HELP :
230
+ continue
231
+ opt_str = option ._short_opts [0 ] if option ._short_opts else option .get_opt_string ()
232
+ option_usage = r .Text ("[" ).append (opt_str , "optparse.args" )
233
+ if option .takes_value ():
234
+ metavar = option .metavar or option .dest .upper () # type: ignore[union-attr]
235
+ option_usage .append (" " ).append (metavar , "optparse.metavar" )
236
+ option_usage .append ("]" )
237
+ if len (usage_lines [- 1 ]) + len (option_usage ) + 1 > self .width :
238
+ usage_lines .append (r .Text (" " * indent ) + option_usage )
239
+ else :
240
+ usage_lines [- 1 ].append (" " ).append (option_usage )
241
+ usage_lines .append (r .Text ())
242
+ return r .Text ("\n " ).join (usage_lines )
243
+
188
244
189
- class IndentedRichHelpFormatter (RichHelpFormatter , optparse . IndentedHelpFormatter ):
245
+ class IndentedRichHelpFormatter (RichHelpFormatter ):
190
246
"""Format help with indented section bodies."""
191
247
192
248
def __init__ (
@@ -198,18 +254,19 @@ def __init__(
198
254
) -> None :
199
255
super ().__init__ (indent_increment , max_help_position , width , short_first )
200
256
201
- def format_usage (self , usage : str ) -> str :
202
- usage = super ().format_usage (usage )
203
- prefix = super ().format_usage ("" ).rstrip ()
257
+ def rich_format_usage (self , usage : str ) -> r .Text :
258
+ usage_template = optparse ._ ("Usage: %s\n " ) # type: ignore[attr-defined]
259
+ usage = usage_template % usage
260
+ prefix = (usage_template % "" ).rstrip ()
204
261
spans = [r .Span (0 , len (prefix ), "optparse.groups" )]
205
- return self . _stringify ( r .Text (usage , spans = spans ) )
262
+ return r .Text (usage , spans = spans )
206
263
207
- def format_heading (self , heading : str ) -> str :
264
+ def rich_format_heading (self , heading : str ) -> r . Text :
208
265
text = r .Text (" " * self .current_indent ).append (f"{ heading } :" , "optparse.groups" )
209
- return self . _stringify ( text ) + "\n "
266
+ return text + r . Text ( "\n " )
210
267
211
268
212
- class TitledRichHelpFormatter (RichHelpFormatter , optparse . TitledHelpFormatter ):
269
+ class TitledRichHelpFormatter (RichHelpFormatter ):
213
270
"""Format help with underlined section headers."""
214
271
215
272
def __init__ (
@@ -221,20 +278,15 @@ def __init__(
221
278
) -> None :
222
279
super ().__init__ (indent_increment , max_help_position , width , short_first )
223
280
224
- def format_usage (self , usage : str ) -> str :
225
- usage_heading = super (). format_usage ( "" ). rstrip ( " \n " )
226
- return f" { usage_heading } { usage } \n "
281
+ def rich_format_usage (self , usage : str ) -> r . Text :
282
+ heading = self . rich_format_heading ( optparse . _ ( "Usage" )) # type: ignore[attr-defined]
283
+ return r . Text . assemble ( heading , " " , usage , " \n ")
227
284
228
- def format_heading (self , heading : str ) -> str :
229
- text = r .Text ().append_tokens (
230
- [
231
- (heading , "optparse.groups" ),
232
- ("\n " , None ),
233
- ("=-" [self .level ] * len (heading ), "optparse.groups" ),
234
- ("\n " , None ),
235
- ]
285
+ def rich_format_heading (self , heading : str ) -> r .Text :
286
+ underline = "=-" [self .level ] * len (heading )
287
+ return r .Text .assemble (
288
+ (heading , "optparse.groups" ), "\n " , (underline , "optparse.groups" ), "\n "
236
289
)
237
- return self ._stringify (text )
238
290
239
291
240
292
if __name__ == "__main__" :
@@ -244,6 +296,7 @@ def format_heading(self, heading: str) -> str:
244
296
formatter = IndentedRichHelpFormatter (),
245
297
prog = "python -m rich_arparse.optparse" ,
246
298
epilog = ":link: https://github.com/hamdanal/rich-argparse#optparse-support." ,
299
+ usage = GENERATE_USAGE ,
247
300
)
248
301
parser .add_option ("--formatter" , metavar = "rich" , help = "A piece of :cake: isn't it? :wink:" )
249
302
parser .add_option (
0 commit comments