cli.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. """
  2. CLI parsing for :command:`pysemver` command.
  3. Each command in :command:`pysemver` is mapped to a ``cmd_`` function.
  4. The :func:`main <semver.cli.main>` function calls
  5. :func:`createparser <semver.cli.createparser>` and
  6. :func:`process <semver.cli.process>` to parse and process
  7. all the commandline options.
  8. The result of each command is printed on stdout.
  9. """
  10. import argparse
  11. import sys
  12. from typing import cast, List, Optional
  13. from .version import Version
  14. from .__about__ import __version__
  15. def cmd_bump(args: argparse.Namespace) -> str:
  16. """
  17. Subcommand: Bumps a version.
  18. Synopsis: bump <PART> <VERSION>
  19. <PART> can be major, minor, patch, prerelease, or build
  20. :param args: The parsed arguments
  21. :return: the new, bumped version
  22. """
  23. maptable = {
  24. "major": "bump_major",
  25. "minor": "bump_minor",
  26. "patch": "bump_patch",
  27. "prerelease": "bump_prerelease",
  28. "build": "bump_build",
  29. }
  30. if args.bump is None:
  31. # When bump is called without arguments,
  32. # print the help and exit
  33. args.parser.parse_args(["bump", "-h"])
  34. ver = Version.parse(args.version)
  35. # get the respective method and call it
  36. func = getattr(ver, maptable[cast(str, args.bump)])
  37. return str(func())
  38. def cmd_check(args: argparse.Namespace) -> None:
  39. """
  40. Subcommand: Checks if a string is a valid semver version.
  41. Synopsis: check <VERSION>
  42. :param args: The parsed arguments
  43. """
  44. if Version.is_valid(args.version):
  45. return None
  46. raise ValueError("Invalid version %r" % args.version)
  47. def cmd_compare(args: argparse.Namespace) -> str:
  48. """
  49. Subcommand: Compare two versions.
  50. Synopsis: compare <VERSION1> <VERSION2>
  51. :param args: The parsed arguments
  52. """
  53. ver1 = Version.parse(args.version1)
  54. return str(ver1.compare(args.version2))
  55. def cmd_nextver(args: argparse.Namespace) -> str:
  56. """
  57. Subcommand: Determines the next version, taking prereleases into account.
  58. Synopsis: nextver <VERSION> <PART>
  59. :param args: The parsed arguments
  60. """
  61. version = Version.parse(args.version)
  62. return str(version.next_version(args.part))
  63. def createparser() -> argparse.ArgumentParser:
  64. """
  65. Create an :class:`argparse.ArgumentParser` instance.
  66. :return: parser instance
  67. """
  68. parser = argparse.ArgumentParser(prog=__package__, description=__doc__)
  69. parser.add_argument(
  70. "--version", action="version", version="%(prog)s " + __version__
  71. )
  72. s = parser.add_subparsers()
  73. # create compare subcommand
  74. parser_compare = s.add_parser("compare", help="Compare two versions")
  75. parser_compare.set_defaults(func=cmd_compare)
  76. parser_compare.add_argument("version1", help="First version")
  77. parser_compare.add_argument("version2", help="Second version")
  78. # create bump subcommand
  79. parser_bump = s.add_parser("bump", help="Bumps a version")
  80. parser_bump.set_defaults(func=cmd_bump)
  81. sb = parser_bump.add_subparsers(title="Bump commands", dest="bump")
  82. # Create subparsers for the bump subparser:
  83. for p in (
  84. sb.add_parser("major", help="Bump the major part of the version"),
  85. sb.add_parser("minor", help="Bump the minor part of the version"),
  86. sb.add_parser("patch", help="Bump the patch part of the version"),
  87. sb.add_parser("prerelease", help="Bump the prerelease part of the version"),
  88. sb.add_parser("build", help="Bump the build part of the version"),
  89. ):
  90. p.add_argument("version", help="Version to raise")
  91. # Create the check subcommand
  92. parser_check = s.add_parser(
  93. "check", help="Checks if a string is a valid semver version"
  94. )
  95. parser_check.set_defaults(func=cmd_check)
  96. parser_check.add_argument("version", help="Version to check")
  97. # Create the nextver subcommand
  98. parser_nextver = s.add_parser(
  99. "nextver", help="Determines the next version, taking prereleases into account."
  100. )
  101. parser_nextver.set_defaults(func=cmd_nextver)
  102. parser_nextver.add_argument("version", help="Version to raise")
  103. parser_nextver.add_argument(
  104. "part", help="One of 'major', 'minor', 'patch', or 'prerelease'"
  105. )
  106. return parser
  107. def process(args: argparse.Namespace) -> str:
  108. """
  109. Process the input from the CLI.
  110. :param args: The parsed arguments
  111. :param parser: the parser instance
  112. :return: result of the selected action
  113. """
  114. if not hasattr(args, "func"):
  115. args.parser.print_help()
  116. raise SystemExit()
  117. # Call the respective function object:
  118. return args.func(args)
  119. def main(cliargs: Optional[List[str]] = None) -> int:
  120. """
  121. Entry point for the application script.
  122. :param list cliargs: Arguments to parse or None (=use :class:`sys.argv`)
  123. :return: error code
  124. """
  125. try:
  126. parser = createparser()
  127. args = parser.parse_args(args=cliargs)
  128. # Save parser instance:
  129. args.parser = parser
  130. result = process(args)
  131. if result is not None:
  132. print(result)
  133. return 0
  134. except (ValueError, TypeError) as err:
  135. print("ERROR", err, file=sys.stderr)
  136. return 2