man_to_argparse.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. #!/usr/bin/env python3
  2. """ Write a python argparse object and text file for a given OptList. """
  3. import argparse
  4. _PY_BEGIN = """#!/usr/bin/env python3
  5. \"\"\" Arguments for the tarsnap CLI. Does nothing else.
  6. WARNING: options starting with "mode-" are a workaround for
  7. argparse not being able to have subparsers which start with a hyphen.
  8. https://bugs.python.org/issue34046
  9. For the real tarsnap options, remove any "mode-" string.
  10. \"\"\"
  11. import argparse
  12. def get_parser():
  13. \"\"\" Create a parser for tarsnap command-line arguments. \"\"\"
  14. parser = argparse.ArgumentParser(prog="tarsnap")
  15. subparsers = parser.add_subparsers()
  16. # Commands available for all modes.
  17. """
  18. _PY_NOARG = """ {parser_name}.add_argument("{opt}", action="store_true",
  19. {spaces}help="{desc}")
  20. """
  21. # Don't add "quotes" around the 'type' argument.
  22. _PY_ARG_TYPE = """ {parser_name}.add_argument("{opt}", metavar="{metavar}",
  23. {spaces}type={argtype},
  24. {spaces}help="{desc}")
  25. """
  26. _PY_SUBPARSER = """
  27. # Commands specific for mode {mode}.
  28. subparser = subparsers.add_parser("{mode}",
  29. {spaces}help="{desc}",
  30. {spaces}description="{desc}")
  31. """
  32. _PY_END = """ return parser
  33. if __name__ == "__main__":
  34. tarsnap_parser = get_parser()
  35. tarsnap_parser.parse_args()
  36. """
  37. def get_argtypestr(arg):
  38. """ Return the type of variable to use for the tarsnap arg as a string. """
  39. if arg in ["bytespercheckpoint", "bytespersecond", "X", "numbytes",
  40. "count"]:
  41. argtypestr = "int"
  42. elif arg in ["cache-dir", "directory"]:
  43. argtypestr = "directory"
  44. elif arg in ["filename", "key-file"]:
  45. argtypestr = "filename"
  46. elif arg in ["archive-name"]:
  47. argtypestr = "archive-name"
  48. elif arg in ["date"]:
  49. argtypestr = "date"
  50. elif arg in ["method:arg", "pattern"]:
  51. argtypestr = "str"
  52. else:
  53. print("Unsupported arg:", arg)
  54. exit(1)
  55. return argtypestr
  56. def get_argtype(arg):
  57. """ Return the type of variable to use for the tarsnap arg. """
  58. argtypestr = get_argtypestr(arg)
  59. if argtypestr == "str":
  60. argtype = str
  61. elif argtypestr == "int":
  62. argtype = int
  63. elif ["directory", "filename", "date",
  64. "archive-name"].index(argtypestr) >= 0:
  65. # No special handling (yet?)
  66. argtype = str
  67. else:
  68. print("Unsupported argtypestr:\t%s\t%s" % (arg, argtypestr))
  69. exit(1)
  70. return argtype
  71. def add_arg(parser_obj, parser_name, optarg):
  72. """ Add an optarg to the parser_obj called parser_name. """
  73. # Skip -h because argparse has its own -h --help.
  74. if optarg.opt == "-h":
  75. return ""
  76. spaces = " " * len("%s.add_argument(" % parser_name)
  77. text = ""
  78. if optarg.arg:
  79. argtype = get_argtype(optarg.arg)
  80. text = _PY_ARG_TYPE.format(parser_name=parser_name, opt=optarg.opt,
  81. spaces=spaces, desc=optarg.desc,
  82. metavar=optarg.arg,
  83. argtype=argtype.__name__)
  84. parser_obj.add_argument(optarg.opt, metavar=optarg.arg,
  85. help=optarg.desc,
  86. type=argtype)
  87. else:
  88. text = _PY_NOARG.format(parser_name=parser_name, opt=optarg.opt,
  89. spaces=spaces, desc=optarg.desc)
  90. parser_obj.add_argument(optarg.opt, action="store_true",
  91. help=optarg.desc)
  92. return text
  93. def is_mode_global(mode):
  94. """ Is this mode global? """
  95. if mode == "":
  96. return True
  97. modestr = mode[0]
  98. if modestr == "(all modes)" or modestr.startswith("(use with"):
  99. return True
  100. return False
  101. def add_global_options(parser_obj, parser_name, optlist):
  102. """ Add all global options to parser_obj. """
  103. relevant = optlist.get_optargs_with_func_modes(is_mode_global)
  104. text = ""
  105. for optarg in relevant:
  106. text += add_arg(parser_obj, parser_name, optarg)
  107. return text
  108. def generate(options, optlist, descs):
  109. """ Generate an argparse object and text for a python file. """
  110. parser = argparse.ArgumentParser(prog="tarsnap")
  111. subparsers = parser.add_subparsers()
  112. text = ""
  113. # Global options
  114. text += add_global_options(parser, "parser", optlist)
  115. for mode in sorted(options["modes"]):
  116. if len(mode) == 1:
  117. modestr = "mode-%s" % mode
  118. else:
  119. modestr = "mode--%s" % mode
  120. desc = descs.get(modestr)
  121. spaces = " " * len("subparser = subparsers.add_parser(")
  122. text += _PY_SUBPARSER.format(mode=modestr, spaces=spaces, desc=desc)
  123. # argparse uses "description" for the main text shown in:
  124. # ./tarsnap.py mode-c --help
  125. # and "help" for the mode-c text displayed in:
  126. # ./tarsnap.py --help
  127. subparser = subparsers.add_parser(modestr, help=desc, description=desc)
  128. # Add options specific to this mode.
  129. relevant = optlist.get_optargs_with_func_modes(
  130. lambda x, m=mode: m in x)
  131. for optarg in relevant:
  132. text += add_arg(subparser, "subparser", optarg)
  133. # Add global options (which will still be valid).
  134. text += add_global_options(subparser, "subparser", optlist)
  135. return parser, text
  136. def write_argparse(filename_py, options, optlist, descs):
  137. """ Write a python file with argparse for tarsnap.
  138. WARNING: options starting with "mode-" are a workaround for
  139. argparse not being able to have subparsers which start with a hyphen.
  140. https://bugs.python.org/issue34046
  141. """
  142. _, parser_text = generate(options, optlist, descs)
  143. # Write argparse file.
  144. with open(filename_py, "wt", encoding="utf-8") as fileobj:
  145. fileobj.write(_PY_BEGIN)
  146. fileobj.write(parser_text)
  147. fileobj.write(_PY_END)