You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

152 lines
5.7 KiB

  1. """Command line client functionality"""
  2. import nilmdb
  3. from nilmdb.utils.printf import *
  4. from nilmdb.utils import datetime_tz
  5. import sys
  6. import re
  7. import argparse
  8. from argparse import ArgumentDefaultsHelpFormatter as def_form
  9. # Valid subcommands. Defined in separate files just to break
  10. # things up -- they're still called with Cmdline as self.
  11. subcommands = [ "info", "create", "list", "metadata", "insert", "extract",
  12. "remove", "destroy" ]
  13. # Import the subcommand modules
  14. subcmd_mods = {}
  15. for cmd in subcommands:
  16. subcmd_mods[cmd] = __import__("nilmdb.cmdline." + cmd, fromlist = [ cmd ])
  17. class JimArgumentParser(argparse.ArgumentParser):
  18. def error(self, message):
  19. self.print_usage(sys.stderr)
  20. self.exit(2, sprintf("error: %s\n", message))
  21. class Cmdline(object):
  22. def __init__(self, argv = None):
  23. self.argv = argv or sys.argv[1:]
  24. self.client = None
  25. def arg_time(self, toparse):
  26. """Parse a time string argument"""
  27. try:
  28. return self.parse_time(toparse).totimestamp()
  29. except ValueError as e:
  30. raise argparse.ArgumentTypeError(sprintf("%s \"%s\"",
  31. str(e), toparse))
  32. def parse_time(self, toparse):
  33. """
  34. Parse a free-form time string and return a datetime_tz object.
  35. If the string doesn't contain a timestamp, the current local
  36. timezone is assumed (e.g. from the TZ env var).
  37. """
  38. # If string isn't "now" and doesn't contain at least 4 digits,
  39. # consider it invalid. smartparse might otherwise accept
  40. # empty strings and strings with just separators.
  41. if toparse != "now" and len(re.findall(r"\d", toparse)) < 4:
  42. raise ValueError("not enough digits for a timestamp")
  43. # Try to just parse the time as given
  44. try:
  45. return datetime_tz.datetime_tz.smartparse(toparse)
  46. except ValueError:
  47. pass
  48. # Try to extract a substring in a condensed format that we expect
  49. # to see in a filename or header comment
  50. res = re.search(r"(^|[^\d])(" # non-numeric or SOL
  51. r"(199\d|2\d\d\d)" # year
  52. r"[-/]?" # separator
  53. r"(0[1-9]|1[012])" # month
  54. r"[-/]?" # separator
  55. r"([012]\d|3[01])" # day
  56. r"[-T ]?" # separator
  57. r"([01]\d|2[0-3])" # hour
  58. r"[:]?" # separator
  59. r"([0-5]\d)" # minute
  60. r"[:]?" # separator
  61. r"([0-5]\d)?" # second
  62. r"([-+]\d\d\d\d)?" # timezone
  63. r")", toparse)
  64. if res is not None:
  65. try:
  66. return datetime_tz.datetime_tz.smartparse(res.group(2))
  67. except ValueError:
  68. pass
  69. # Could also try to successively parse substrings, but let's
  70. # just give up for now.
  71. raise ValueError("unable to parse timestamp")
  72. def time_string(self, timestamp):
  73. """
  74. Convert a Unix timestamp to a string for printing, using the
  75. local timezone for display (e.g. from the TZ env var).
  76. """
  77. dt = datetime_tz.datetime_tz.fromtimestamp(timestamp)
  78. return dt.strftime("%a, %d %b %Y %H:%M:%S.%f %z")
  79. def parser_setup(self):
  80. self.parser = JimArgumentParser(add_help = False,
  81. formatter_class = def_form)
  82. group = self.parser.add_argument_group("General options")
  83. group.add_argument("-h", "--help", action='help',
  84. help='show this help message and exit')
  85. group.add_argument("-V", "--version", action="version",
  86. version = nilmdb.__version__)
  87. group = self.parser.add_argument_group("Server")
  88. group.add_argument("-u", "--url", action="store",
  89. default="http://localhost:12380/",
  90. help="NilmDB server URL (default: %(default)s)")
  91. sub = self.parser.add_subparsers(title="Commands",
  92. dest="command",
  93. description="Specify --help after "
  94. "the command for command-specific "
  95. "options.")
  96. # Set up subcommands (defined in separate files)
  97. for cmd in subcommands:
  98. subcmd_mods[cmd].setup(self, sub)
  99. def die(self, formatstr, *args):
  100. fprintf(sys.stderr, formatstr + "\n", *args)
  101. if self.client:
  102. self.client.close()
  103. sys.exit(-1)
  104. def run(self):
  105. # Clear cached timezone, so that we can pick up timezone changes
  106. # while running this from the test suite.
  107. datetime_tz._localtz = None
  108. # Run parser
  109. self.parser_setup()
  110. self.args = self.parser.parse_args(self.argv)
  111. # Run arg verify handler if there is one
  112. if "verify" in self.args:
  113. self.args.verify(self)
  114. self.client = nilmdb.Client(self.args.url)
  115. # Make a test connection to make sure things work
  116. try:
  117. server_version = self.client.version()
  118. except nilmdb.client.Error as e:
  119. self.die("error connecting to server: %s", str(e))
  120. # Now dispatch client request to appropriate function. Parser
  121. # should have ensured that we don't have any unknown commands
  122. # here.
  123. retval = self.args.handler(self) or 0
  124. self.client.close()
  125. sys.exit(retval)