enrich_formatter.py 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. #!/usr/bin/env python3
  2. # SPDX-License-Identifier: GPL-2.0
  3. # Copyright (c) 2025 by Mauro Carvalho Chehab <mchehab@kernel.org>.
  4. """
  5. Ancillary argparse HelpFormatter class that works on a similar way as
  6. argparse.RawDescriptionHelpFormatter, e.g. description maintains line
  7. breaks, but it also implement transformations to the help text. The
  8. actual transformations ar given by enrich_text(), if the output is tty.
  9. Currently, the follow transformations are done:
  10. - Positional arguments are shown in upper cases;
  11. - if output is TTY, ``var`` and positional arguments are shown prepended
  12. by an ANSI SGR code. This is usually translated to bold. On some
  13. terminals, like, konsole, this is translated into a colored bold text.
  14. """
  15. import argparse
  16. import re
  17. import sys
  18. class EnrichFormatter(argparse.HelpFormatter):
  19. """
  20. Better format the output, making easier to identify the positional args
  21. and how they're used at the __doc__ description.
  22. """
  23. def __init__(self, *args, **kwargs):
  24. """
  25. Initialize class and check if is TTY.
  26. """
  27. super().__init__(*args, **kwargs)
  28. self._tty = sys.stdout.isatty()
  29. def enrich_text(self, text):
  30. r"""
  31. Handle ReST markups (currently, only \`\`text\`\` markups).
  32. """
  33. if self._tty and text:
  34. # Replace ``text`` with ANSI SGR (bold)
  35. return re.sub(r'\`\`(.+?)\`\`',
  36. lambda m: f'\033[1m{m.group(1)}\033[0m', text)
  37. return text
  38. def _fill_text(self, text, width, indent):
  39. """
  40. Enrich descriptions with markups on it.
  41. """
  42. enriched = self.enrich_text(text)
  43. return "\n".join(indent + line for line in enriched.splitlines())
  44. def _format_usage(self, usage, actions, groups, prefix):
  45. """
  46. Enrich positional arguments at usage: line.
  47. """
  48. prog = self._prog
  49. parts = []
  50. for action in actions:
  51. if action.option_strings:
  52. opt = action.option_strings[0]
  53. if action.nargs != 0:
  54. opt += f" {action.dest.upper()}"
  55. parts.append(f"[{opt}]")
  56. else:
  57. # Positional argument
  58. parts.append(self.enrich_text(f"``{action.dest.upper()}``"))
  59. usage_text = f"{prefix or 'usage: '} {prog} {' '.join(parts)}\n"
  60. return usage_text
  61. def _format_action_invocation(self, action):
  62. """
  63. Enrich argument names.
  64. """
  65. if not action.option_strings:
  66. return self.enrich_text(f"``{action.dest.upper()}``")
  67. return ", ".join(action.option_strings)