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.

241 lines
10 KiB

10 years ago
  1. import argparse
  2. import sys
  3. import unittest
  4. from argparse import ArgumentParser
  5. from gooey import GooeyParser
  6. from gooey.python_bindings import argparse_to_json
  7. from gooey.util.functional import getin
  8. class TestArgparse(unittest.TestCase):
  9. def test_mutex_groups_conversion(self):
  10. """
  11. Ensure multiple mutex groups are processed correctly.
  12. """
  13. parser = ArgumentParser()
  14. g1 = parser.add_mutually_exclusive_group(required=True)
  15. g1.add_argument('--choose1')
  16. g1.add_argument('--choose2')
  17. g2 = parser.add_mutually_exclusive_group(required=True)
  18. g2.add_argument('--choose3')
  19. g2.add_argument('--choose4')
  20. output = argparse_to_json.process(parser, {}, {}, {})
  21. # assert that we get two groups of two choices back
  22. items = output[0]['items']
  23. self.assertTrue(len(items) == 2)
  24. group1 = items[0]
  25. group2 = items[1]
  26. self.assertTrue(['--choose1'] in group1['data']['commands'])
  27. self.assertTrue(['--choose2'] in group1['data']['commands'])
  28. self.assertTrue(['--choose3'] in group2['data']['commands'])
  29. self.assertTrue(['--choose4'] in group2['data']['commands'])
  30. self.assertTrue(group1['type'] == 'RadioGroup')
  31. self.assertTrue(group2['type'] == 'RadioGroup')
  32. def test_json_iterable_conversion(self):
  33. """
  34. Issue #312 - tuples weren't being coerced to list during argparse
  35. conversion causing downstream issues when concatenating
  36. """
  37. # our original functionality accepted only lists as the choices arg
  38. parser = ArgumentParser()
  39. parser.add_argument("-foo", choices=['foo','bar', 'baz'])
  40. result = argparse_to_json.action_to_json(parser._actions[-1], "Dropdown", {})
  41. choices = result['data']['choices']
  42. self.assertTrue(isinstance(choices, list))
  43. self.assertEqual(choices, ['foo','bar', 'baz'])
  44. # Now we allow tuples as well.
  45. parser = ArgumentParser()
  46. parser.add_argument("-foo", choices=('foo','bar', 'baz'))
  47. result = argparse_to_json.action_to_json(parser._actions[-1], "Dropdown", {})
  48. choices = result['data']['choices']
  49. self.assertTrue(isinstance(choices, list))
  50. self.assertEqual(choices, ['foo','bar', 'baz'])
  51. def test_choice_string_cooersion(self):
  52. """
  53. Issue 321 - must coerce choice types to string to support wx.ComboBox
  54. """
  55. parser = ArgumentParser()
  56. parser.add_argument('--foo', default=1, choices=[1, 2, 3])
  57. choice_action = parser._actions[-1]
  58. result = argparse_to_json.action_to_json(choice_action, 'Dropdown', {})
  59. self.assertEqual(getin(result, ['data', 'choices']), ['1', '2', '3'])
  60. # default value is also converted to a string type
  61. self.assertEqual(getin(result, ['data', 'default']), '1')
  62. def test_choice_string_cooersion_no_default(self):
  63. """
  64. Make sure that choice types without a default don't create
  65. the literal string "None" but stick with the value None
  66. """
  67. parser = ArgumentParser()
  68. parser.add_argument('--foo', choices=[1, 2, 3])
  69. choice_action = parser._actions[-1]
  70. result = argparse_to_json.action_to_json(choice_action, 'Dropdown', {})
  71. self.assertEqual(getin(result, ['data', 'default']), None)
  72. def test_listbox_defaults_cast_correctly(self):
  73. """
  74. Issue XXX - defaults supplied in a list were turned into a string
  75. wholesale (list and all). The defaults should be stored as a list
  76. proper with only the _internal_ values coerced to strings.
  77. """
  78. parser = GooeyParser()
  79. parser.add_argument('--foo', widget="Listbox", nargs="*", choices=[1, 2, 3], default=[1, 2])
  80. choice_action = parser._actions[-1]
  81. result = argparse_to_json.action_to_json(choice_action, 'Listbox', {})
  82. self.assertEqual(getin(result, ['data', 'default']), ['1', '2'])
  83. def test_listbox_single_default_cast_correctly(self):
  84. """
  85. Single arg defaults to listbox should be wrapped in a list and
  86. their contents coerced as usual.
  87. """
  88. parser = GooeyParser()
  89. parser.add_argument('--foo', widget="Listbox",
  90. nargs="*", choices=[1, 2, 3], default="sup")
  91. choice_action = parser._actions[-1]
  92. result = argparse_to_json.action_to_json(choice_action, 'Listbox', {})
  93. self.assertEqual(getin(result, ['data', 'default']), ['sup'])
  94. def test_non_data_defaults_are_dropped_entirely(self):
  95. """
  96. This is a refinement in understanding of Issue #147
  97. Caused by Issue 377 - passing arbitrary objects as defaults
  98. causes failures.
  99. """
  100. # passing plain data to cleaning function results in plain data
  101. # being returned
  102. data = ['abc',
  103. 123,
  104. ['a', 'b'],
  105. [1, 2, 3]]
  106. for datum in data:
  107. result = argparse_to_json.clean_default(datum)
  108. self.assertEqual(result, datum)
  109. # passing in complex objects results in None
  110. objects = [sys.stdout, sys.stdin, object(), max, min]
  111. for obj in objects:
  112. result = argparse_to_json.clean_default(obj)
  113. self.assertEqual(result, None)
  114. def test_suppress_is_removed_as_default_value(self):
  115. """
  116. Issue #469
  117. Argparse uses the literal string ==SUPPRESS== as an internal flag.
  118. When encountered in Gooey, these should be dropped and mapped to `None`.
  119. """
  120. parser = ArgumentParser(prog='test_program')
  121. parser.add_argument("--foo", default=argparse.SUPPRESS)
  122. parser.add_argument('--version', action='version', version='1.0')
  123. result = argparse_to_json.convert(parser, num_required_cols=2, num_optional_cols=2)
  124. groups = getin(result, ['widgets', 'test_program', 'contents'])
  125. for item in groups[0]['items']:
  126. self.assertEqual(getin(item, ['data', 'default']), None)
  127. def test_textinput_with_list_default_mapped_to_cli_friendly_value(self):
  128. """
  129. Issue: #500
  130. Using nargs and a `default` value with a list causes the literal list string
  131. to be put into the UI.
  132. """
  133. testcases = [
  134. {'nargs': '+', 'default': ['a b', 'c'], 'gooey_default': '"a b" "c"', 'w': 'TextField'},
  135. {'nargs': '*', 'default': ['a b', 'c'], 'gooey_default': '"a b" "c"', 'w': 'TextField'},
  136. {'nargs': '...', 'default': ['a b', 'c'], 'gooey_default': '"a b" "c"', 'w': 'TextField'},
  137. {'nargs': 2, 'default': ['a b', 'c'], 'gooey_default': '"a b" "c"', 'w': 'TextField'},
  138. # TODO: this demos the current nargs behavior for string defaults, but
  139. # TODO: it is wrong! These should be wrapped in quotes so spaces aren't
  140. # TODO: interpreted as unique arguments.
  141. {'nargs': '+', 'default': 'a b', 'gooey_default': 'a b', 'w': 'TextField'},
  142. {'nargs': '*', 'default': 'a b', 'gooey_default': 'a b', 'w': 'TextField'},
  143. {'nargs': '...', 'default': 'a b', 'gooey_default': 'a b', 'w': 'TextField'},
  144. {'nargs': 1, 'default': 'a b', 'gooey_default': 'a b', 'w': 'TextField'},
  145. # Listbox has special nargs handling which keeps the list in tact.
  146. {'nargs': '+', 'default': ['a b', 'c'], 'gooey_default': ['a b', 'c'], 'w': 'Listbox'},
  147. {'nargs': '*', 'default': ['a b', 'c'], 'gooey_default': ['a b', 'c'], 'w': 'Listbox'},
  148. {'nargs': '...', 'default': ['a b', 'c'], 'gooey_default': ['a b', 'c'],'w': 'Listbox'},
  149. {'nargs': 2, 'default': ['a b', 'c'], 'gooey_default': ['a b', 'c'], 'w': 'Listbox'},
  150. {'nargs': '+', 'default': 'a b', 'gooey_default': ['a b'], 'w': 'Listbox'},
  151. {'nargs': '*', 'default': 'a b', 'gooey_default': ['a b'], 'w': 'Listbox'},
  152. {'nargs': '...', 'default': 'a b', 'gooey_default': ['a b'], 'w': 'Listbox'},
  153. {'nargs': 1, 'default': 'a b', 'gooey_default': ['a b'], 'w': 'Listbox'},
  154. ]
  155. for case in testcases:
  156. with self.subTest(case):
  157. parser = ArgumentParser(prog='test_program')
  158. parser.add_argument('--foo', nargs=case['nargs'], default=case['default'])
  159. action = parser._actions[-1]
  160. result = argparse_to_json.handle_default(action, case['w'])
  161. self.assertEqual(result, case['gooey_default'])
  162. def test_nargs(self):
  163. """
  164. so there are just a few simple rules here:
  165. if nargs in [*, N, +, remainder]:
  166. default MUST be a list OR we must map it to one
  167. action:_StoreAction
  168. - nargs '?'
  169. - default:validate list is invalid
  170. - default:coerce stringify
  171. - nargs #{*, N, +, REMAINDER}
  172. - default:validate None
  173. - default:coerce
  174. if string: stringify
  175. if list: convert from list to cli style input string
  176. action:_StoreConstAction
  177. - nargs: invalid
  178. - defaults:stringify
  179. action:{_StoreFalseAction, _StoreTrueAction}
  180. - nargs: invalid
  181. - defaults:validate: require bool
  182. - defaults:coerce: no stringify; leave bool
  183. action:_CountAction
  184. - nargs: invalid
  185. - default:validate: must be numeric index within range OR None
  186. - default:coerce: integer or None
  187. action:_AppendAction
  188. TODO: NOT CURRENTLY SUPPORTED BY GOOEY
  189. nargs behavior is weird and needs to be understood.
  190. - nargs
  191. action:CustomUserAction:
  192. - nargs: no way to know expected behavior. Ignore
  193. - default: jsonify type if possible.
  194. """
  195. parser = ArgumentParser()
  196. parser.add_argument(
  197. '--bar',
  198. nargs='+',
  199. choices=["one", "two"],
  200. default="one",
  201. )