-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature request] Reuse application console for argparse output #91
Comments
I think someone asked for this before (or something similar). I don't oppose adding a way for the user of the library to generate an svg out of the help text (and maybe other formats available in rich). I don't know how to this correctly though. In your example, you do
Please let me know if you have ideas on how we can have something that works seamlessly. |
I spent some more time looking into the
Using the aforementioned API it should be possible to overlay the Here is an example that puts into practice what I just described and addresses both of your concerns. It uses the You should be able to just run it and get a python example.py python example.py --help Edit: I did not test the behavior with an existing console theme, but should be very simple to test and validate... Complete (revised) example
# example.py
import argparse
from sys import exit
from rich.console import Console
from rich.theme import Theme
from rich_argparse import RichHelpFormatter
class CustomHelpFormatter(RichHelpFormatter):
_CUSTOM_CONSOLE: Console = None
_THEME_PUSHED: bool = False
@classmethod
def set_custom_console(cls, console: Console):
"""Store custom console and backup the theme stack"""
cls._CUSTOM_CONSOLE = console
def __init__(
self,
prog: str,
indent_increment: int = 2,
max_help_position: int = 24,
width: int = None,
) -> None:
super().__init__(prog, indent_increment, max_help_position, width)
if self._CUSTOM_CONSOLE:
# Init console
self._console = self._CUSTOM_CONSOLE
# apply theme (class variable prevents multiple pushes when reused)
if not CustomHelpFormatter._THEME_PUSHED:
self._console.push_theme(Theme(self.styles))
CustomHelpFormatter._THEME_PUSHED = True
def __del__(self):
if self._CUSTOM_CONSOLE:
# Revert theme
if CustomHelpFormatter._THEME_PUSHED:
CustomHelpFormatter._THEME_PUSHED = False
self._console.pop_theme()
return super().__del__()
class MyApplication:
def __init__(self, record: bool = True) -> None:
self.console = Console(record=record)
self._record = record
def _get_args(self) -> argparse.Namespace:
"""Configure parser and get CLI args"""
CustomHelpFormatter.set_custom_console(self.console)
parser = argparse.ArgumentParser(formatter_class=CustomHelpFormatter)
parser.add_argument("--flag-1", action="store_true", help="Set flag 1 to true")
parser.add_argument("--flag-2", action="store_true", help="Set flag 2 to true")
parser.add_argument("--flag-3", action="store_true", help="Set flag 3 to true")
# Override exit method
parser.exit = self._on_parser_exit
return parser.parse_args()
def _on_parser_exit(self, status=0, message=None) -> None:
"""Custom parser exit that adds step to save the console"""
if message:
self.console.print(message)
if self._record:
self.console.save_svg("console.svg")
exit(status)
def run(self) -> None:
"""Main application entrypoint"""
args = self._get_args()
self.console.print("[green]Hello![/]")
self.console.print(f"Flag 1 set to: {args.flag_1}")
self.console.print(f"Flag 2 set to: {args.flag_2}")
self.console.print(f"Flag 3 set to: {args.flag_3}")
if self._record:
self.console.save_svg("console.svg")
if __name__ == "__main__":
app = MyApplication()
app.run() |
This is very useful, thank you. I'll start by allowing the console to be passed to the constructor of the formatter and see what we can come up for the SVG generation. This allows you to at least avoid the hack with the class variable. Note that when I run your example I get a traceback: With `python example.py`
With `python example.py --help`
Also I noticed you generate the SVG in both cases above. Is this intentional? What is your use case? I think the previous request I got for this feature was to include the SVG in the documentation; in this case the SVG should not be generated when your users run the application but rather with a special option or an env var. I am asking because this will affect how I will introduce the feature in rich-argparse. |
OK so I have created something :) import argparse
from rich.console import Console
from rich_argparse import HelpPreviewAction, RichHelpFormatter
class MyApplication:
def __init__(self):
self.console = Console()
def _get_args(self) -> argparse.Namespace:
"""Configure parser and get CLI args"""
parser = argparse.ArgumentParser(
formatter_class=lambda prog: RichHelpFormatter(prog, console=self.console)
)
parser.add_argument("--flag-1", action="store_true", help="Set flag 1 to true")
parser.add_argument("--flag-2", action="store_true", help="Set flag 2 to true")
parser.add_argument("--flag-3", action="store_true", help="Set flag 3 to true")
parser.add_argument(
"--my-secret-help-preview-generator", action=HelpPreviewAction, console=self.console
)
return parser.parse_args()
def run(self) -> None:
"""Main application entrypoint"""
args = self._get_args()
self.console.print("[green]Hello![/]")
self.console.print(f"Flag 1 set to: {args.flag_1}")
self.console.print(f"Flag 2 set to: {args.flag_2}")
self.console.print(f"Flag 3 set to: {args.flag_3}")
if __name__ == "__main__":
app = MyApplication()
app.run() Then using the script like this:
If you want to generate an HTML instead, simply replace You can also pass keyword arguments to the parser.add_argument(
"--generate-whatever",
action=HelpPreviewAction,
console=self.console,
save_kwds={"theme": DIMMED_MONOKAI, "title": "My Title"},
) You can also set the default output file using parser.add_argument("--generate-whatever", action=HelpPreviewAction, console=self.console, path="console.svg") then you can simply call the script with |
Great, I've seen the changes in #92 and it looks promising.
Right... I added the
Yes, this is intentional. The use case, as per the previous request, is for documentation only. I simplified a lot the example for brevity's sake, but indeed I am using a special option for it. Regarding your proposal for #93, it seems like it should do the job for my use case. I am personally not a huge fan of the lambda as the Do you have a timeline for when this (and previous) changes might make it to release? Just to understand whether I should wait or just install from source for the moment... |
I changed the implementation of the action to not take a console. You no longer need to use the lambda. This is also simpler and less error prone, we can always revisit in the future if someone needs a custom console in the action. Could you please test if the main branch works for your case? You can find the docs here.
Soonish. hopefully by next week if everything goes well. |
Tested and works. The only downside of not being able to pass a console (for my specific use case) is that anything that is printed before the Thank you for the fix! |
First and foremost, I love the tool, kudos to you.
Is there any out-of-the-box way to reuse the main application console for the
argparse
output?I would like to generate SVGs using the
save_svg()
method of the console, but theRichHelpFormatter
instantiates its ownrich.Console
when instantiated byargparse
.In the meantime, I came up with a not-so-clean solution by injecting my console as a class variable, and using that when instantiating
the formatter:
And then use it like this:
The text was updated successfully, but these errors were encountered: