parseargs.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. # The contents of this file are subject to the BitTorrent Open Source License
  2. # Version 1.1 (the License). You may not copy or use this file, in either
  3. # source code or executable form, except in compliance with the License. You
  4. # may obtain a copy of the License at http://www.bittorrent.com/license/.
  5. #
  6. # Software distributed under the License is distributed on an AS IS basis,
  7. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  8. # for the specific language governing rights and limitations under the
  9. # License.
  10. # Written by Bill Bumgarner and Bram Cohen
  11. # Dave's comments:
  12. # makeHelp has no less than 4 elif's based on uiname. Doh.
  13. #
  14. # I like the function parseargs. It makes no UI-specific assumptions.
  15. from types import *
  16. from cStringIO import StringIO
  17. from BTL.translation import _
  18. from BTL.obsoletepythonsupport import *
  19. from BitTorrent import BTFailure
  20. from BTL.bencode import bdecode
  21. from BitTorrent.platform import is_frozen_exe
  22. from BTL.exceptions import str_exc
  23. class UsageException(BTFailure):
  24. pass
  25. def makeHelp(uiname, defaults):
  26. ret = u''
  27. ret += (_("Usage: %s ") % uiname)
  28. if uiname.startswith('launchmany'):
  29. ret += _("[OPTIONS] [TORRENTDIRECTORY]\n\n")
  30. ret += _("If a non-option argument is present it's taken as the value\n"
  31. "of the torrent_dir option.\n")
  32. elif uiname == 'bittorrent-tracker' or uiname == 'test-client':
  33. ret += _("OPTIONS")
  34. elif uiname == 'bittorrent':
  35. ret += _("[OPTIONS] [TORRENTFILES]\n")
  36. elif uiname.startswith('bittorrent'):
  37. ret += _("[OPTIONS] [TORRENTFILE]\n")
  38. elif uiname.startswith('maketorrent'):
  39. ret += _("[OPTION] TRACKER_URL FILE [FILE]\n")
  40. ret += '\n'
  41. ret += _("arguments are -\n") + formatDefinitions(defaults, 80)
  42. return ret
  43. def printHelp(uiname, defaults):
  44. print makeHelp(uiname, defaults)
  45. def formatDefinitions(options, COLS):
  46. s = u''
  47. indent = u" " * 10
  48. width = COLS - 11
  49. if width < 15:
  50. width = COLS - 2
  51. indent = " "
  52. for option in options:
  53. (longname, default, doc) = option
  54. if doc == '':
  55. continue
  56. s += u'--' + longname
  57. is_boolean = type(default) is bool
  58. if is_boolean:
  59. s += u', --no_' + longname
  60. else:
  61. s += u' <arg>'
  62. s += u'\n'
  63. if default is not None:
  64. doc += _(u" (defaults to ") + repr(default) + u')'
  65. i = 0
  66. for word in doc.split():
  67. if i == 0:
  68. s += indent + word
  69. i = len(word)
  70. elif i + len(word) >= width:
  71. s += u'\n' + indent + word
  72. i = len(word)
  73. else:
  74. s += u' ' + word
  75. i += len(word) + 1
  76. s += u'\n\n'
  77. return s
  78. def usage(str):
  79. raise UsageException(str)
  80. def format_key(key):
  81. if len(key) == 1:
  82. return '-%s'%key
  83. else:
  84. return '--%s'%key
  85. def parseargs(argv, defaults, minargs=None, maxargs=None, presets=None ):
  86. """This function parses command-line arguments and uses them to override
  87. the presets which in turn override the defaults (see defaultargs.py).
  88. As currently used, the presets come from a config file (see
  89. configfile.py).
  90. Options have the form:
  91. --option value
  92. where the word option is replaced with the option name, etc.
  93. If a string or number appears on the line without being preceeded
  94. by a --option, then the string or number is an argument.
  95. @param argv: command-line arguments. Command-line options override
  96. defaults and presets.
  97. @param defaults: list of (optionname,value,documentation) 3-tuples.
  98. @param minargs: minimum number of arguments in argv.
  99. @param maxargs: maximum number of arguments in argv.
  100. @param presets: a dict containing option-value pairs. Presets
  101. typically come from a config file. Presets override defaults.
  102. @return: the pair (config,args) where config is a dict containing
  103. option-value pairs, and args is a list of the arguments in the
  104. order they appeared in argv.
  105. """
  106. assert type(argv)==list
  107. assert type(defaults)==list
  108. assert minargs is None or type(minargs) in (int,long) and minargs>=0
  109. assert maxargs is None or type(maxargs) in (int,long) and maxargs>=minargs
  110. assert presets is None or type(presets)==dict
  111. config = {}
  112. for option in defaults:
  113. longname, default, doc = option
  114. config[longname] = default
  115. args = []
  116. pos = 0
  117. if presets is None:
  118. presets = {}
  119. else:
  120. presets = presets.copy()
  121. while pos < len(argv):
  122. if argv[pos][:1] != '-': # not a cmdline option
  123. args.append(argv[pos])
  124. pos += 1
  125. else:
  126. key, value = None, None
  127. if argv[pos].startswith('--'): # --aaa 1
  128. if argv[pos].startswith('--no_'):
  129. key = argv[pos][5:]
  130. boolval = False
  131. else:
  132. key = argv[pos][2:]
  133. boolval = True
  134. if key not in config:
  135. raise UsageException(_("unknown option ") + format_key(key))
  136. if type(config[key]) is bool: # boolean cmd line switch, no value
  137. value = boolval
  138. pos += 1
  139. else: # --argument value
  140. if pos == len(argv) - 1:
  141. usage(_("parameter passed in at end with no value"))
  142. key, value = argv[pos][2:], argv[pos+1]
  143. pos += 2
  144. elif argv[pos][:1] == '-':
  145. key = argv[pos][1:2]
  146. if len(argv[pos]) > 2: # -a1
  147. value = argv[pos][2:]
  148. pos += 1
  149. else: # -a 1
  150. if pos == len(argv) - 1:
  151. usage(_("parameter passed in at end with no value"))
  152. value = argv[pos+1]
  153. pos += 2
  154. else:
  155. raise UsageException(_("command line parsing failed at ")+argv[pos])
  156. presets[key] = value
  157. parse_options(config, presets, None)
  158. config.update(presets)
  159. # if a key appears in the config with a None value then this is because
  160. # the key appears in the defaults with a None value and the value was
  161. # not provided by the user. keys appearing in defaults with a none
  162. # value are REQUIRED arguments.
  163. for key, value in config.items():
  164. if value is None:
  165. usage(_("Option %s is required.") % format_key(key))
  166. if minargs is not None and len(args) < minargs:
  167. usage(_("Must supply at least %d arguments.") % minargs)
  168. if maxargs is not None and len(args) > maxargs:
  169. usage(_("Too many arguments - %d maximum.") % maxargs)
  170. return (config, args)
  171. def parse_options(defaults, newvalues, encoding):
  172. """Given the type provided by the default value, this tries to cast/convert
  173. the corresponding newvalue to the type of the default value.
  174. By calling eval() on it, in some cases!
  175. Entertainly, newvalue sometimes holds strings and, apparently,
  176. sometimes holds values which have already been cast appropriately.
  177. This function is like a boat made of shit, floating on a river of shit.
  178. @param defaults: dict of key-value pairs where value is the default.
  179. @param newvalues: dict of key-value pairs which override the default.
  180. """
  181. assert type(defaults) == dict
  182. assert type(newvalues) == dict
  183. for key, value in newvalues.iteritems():
  184. if not defaults.has_key(key):
  185. raise UsageException(_("unknown option ") + format_key(key))
  186. try:
  187. t = type(defaults[key])
  188. if t is bool:
  189. if value in ('True', '1', True):
  190. value = True
  191. else:
  192. value = False
  193. newvalues[key] = value
  194. elif t in (StringType, NoneType):
  195. # force ASCII
  196. newvalues[key] = value.decode('ascii').encode('ascii')
  197. elif t in (IntType, LongType):
  198. if value == 'False':
  199. newvalues[key] == 0
  200. elif value == 'True':
  201. newvalues[key] == 1
  202. else:
  203. newvalues[key] = int(value)
  204. elif t is FloatType:
  205. newvalues[key] = float(value)
  206. elif t in (ListType, TupleType, DictType):
  207. if type(value) == StringType:
  208. try:
  209. n = eval(value)
  210. assert type(n) == t
  211. newvalues[key] = n
  212. except:
  213. newvalues[key] = t()
  214. elif t is UnicodeType:
  215. if type(value) == StringType:
  216. try:
  217. newvalues[key] = value.decode(encoding)
  218. except:
  219. newvalues[key] = value.decode('ascii')
  220. else:
  221. raise TypeError, str(t)
  222. except ValueError, e:
  223. raise UsageException(_("wrong format of %s - %s") % (format_key(key), str_exc(e)))