argparse_tests.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. """Tests for cement.ext.ext_argparse."""
  2. import os
  3. import sys
  4. import re
  5. from argparse import ArgumentError
  6. from cement.ext.ext_argparse import ArgparseArgumentHandler
  7. from cement.ext.ext_argparse import ArgparseController, expose
  8. from cement.ext.ext_argparse import _clean_label, _clean_func
  9. from cement.utils import test
  10. from cement.utils.misc import rando, init_defaults
  11. from cement.core import handler
  12. from cement.core.exc import InterfaceError, FrameworkError
  13. APP = rando()[:12]
  14. if (sys.version_info[0] > 3 and sys.version_info[1] >= 4):
  15. ARGPARSE_SUPPORTS_DEFAULTS = True
  16. else:
  17. ARGPARSE_SUPPORTS_DEFAULTS = False
  18. class Base(ArgparseController):
  19. class Meta:
  20. label = 'base'
  21. arguments = [
  22. (['--foo'], dict(dest='foo')),
  23. ]
  24. @expose(hide=True, help="this help doesn't get seen")
  25. def default(self):
  26. return "Inside Base.default"
  27. @expose()
  28. def cmd1(self):
  29. return "Inside Base.cmd1"
  30. @expose()
  31. def command_with_dashes(self):
  32. return "Inside Base.command_with_dashes"
  33. class Second(ArgparseController):
  34. class Meta:
  35. label = 'second'
  36. stacked_on = 'base'
  37. stacked_type = 'embedded'
  38. arguments = [
  39. (['--foo2'], dict(dest='foo2')),
  40. ]
  41. @expose(
  42. arguments=[
  43. (['--cmd2-foo'],
  44. dict(help='cmd2 sub-command only options', dest='cmd2_foo')),
  45. ]
  46. )
  47. def cmd2(self):
  48. if self.app.pargs.cmd2_foo:
  49. return "Inside Second.cmd2 : Foo > %s" % self.app.pargs.cmd2_foo
  50. else:
  51. return "Inside Second.cmd2"
  52. class Third(ArgparseController):
  53. class Meta:
  54. label = 'third'
  55. stacked_on = 'base'
  56. stacked_type = 'nested'
  57. arguments = [
  58. (['--foo3'], dict(dest='foo3')),
  59. ]
  60. @expose(hide=True)
  61. def default(self):
  62. return "Inside Third.default"
  63. @expose()
  64. def cmd3(self):
  65. return "Inside Third.cmd3"
  66. class Fourth(ArgparseController):
  67. class Meta:
  68. label = 'fourth'
  69. stacked_on = 'third'
  70. stacked_type = 'embedded'
  71. hide = True
  72. help = "this help doesn't get seen cause we're hiding"
  73. arguments = [
  74. (['--foo4'], dict(dest='foo4')),
  75. ]
  76. @expose()
  77. def cmd4(self):
  78. return "Inside Fourth.cmd4"
  79. class Fifth(ArgparseController):
  80. class Meta:
  81. label = 'fifth'
  82. stacked_on = 'third'
  83. stacked_type = 'nested'
  84. hide = True
  85. help = "this help isn't seen... i'm hiding"
  86. arguments = [
  87. (['--foo5'], dict(dest='foo5')),
  88. ]
  89. @expose(hide=True)
  90. def default(self):
  91. return "Inside Fifth.default"
  92. @expose()
  93. def cmd5(self):
  94. return "Inside Fifth.cmd5"
  95. class Sixth(ArgparseController):
  96. class Meta:
  97. label = 'sixth'
  98. stacked_on = 'fifth'
  99. stacked_type = 'nested'
  100. arguments = [
  101. (['--foo6'], dict(dest='foo6')),
  102. ]
  103. @expose(hide=True)
  104. def default(self):
  105. return "Inside Sixth.default"
  106. @expose()
  107. def cmd6(self):
  108. return "Inside Sixth.cmd6"
  109. class Seventh(ArgparseController):
  110. class Meta:
  111. label = 'seventh'
  112. stacked_on = 'fourth'
  113. stacked_type = 'embedded'
  114. arguments = [
  115. (['--foo7'], dict(dest='foo7')),
  116. ]
  117. @expose()
  118. def cmd7(self):
  119. return "Inside Seventh.cmd7"
  120. class Unstacked(ArgparseController):
  121. class Meta:
  122. label = 'unstacked'
  123. stacked_on = None
  124. arguments = [
  125. (['--foo6'], dict(dest='foo6')),
  126. ]
  127. class BadStackType(ArgparseController):
  128. class Meta:
  129. label = 'bad_stack_type'
  130. stacked_on = 'base'
  131. stacked_type = 'bogus_stacked_type'
  132. arguments = [
  133. (['--foo6'], dict(dest='foo6')),
  134. ]
  135. class DuplicateArguments(ArgparseController):
  136. class Meta:
  137. label = 'duplicate_arguments'
  138. arguments = [
  139. (['--foo'], dict(dest='foo')),
  140. ]
  141. class ControllerCommandDuplicateArguments(ArgparseController):
  142. class Meta:
  143. label = 'controller_command_duplicate_arguments'
  144. @expose(
  145. arguments=[
  146. (['--foo'], dict(dest='foo')),
  147. (['--foo'], dict(dest='foo')),
  148. ]
  149. )
  150. def sub_command(self):
  151. pass
  152. class AlternativeDefault(ArgparseController):
  153. class Meta:
  154. label = 'alternative_default'
  155. default_func = 'alternative_default'
  156. stacked_on = 'base'
  157. stacked_type = 'nested'
  158. @expose(hide=True)
  159. def alternative_default(self):
  160. return "Inside AlternativeDefault.alternative_default"
  161. class BadAlternativeDefault(ArgparseController):
  162. class Meta:
  163. label = 'bad_alternative_default'
  164. default_func = 'bogus_default'
  165. stacked_on = 'base'
  166. stacked_type = 'nested'
  167. class Aliases(ArgparseController):
  168. class Meta:
  169. label = 'aliases'
  170. aliases = ['aliases-controller', 'ac']
  171. stacked_on = 'base'
  172. stacked_type = 'nested'
  173. @expose(aliases=['aliases-cmd-1', 'ac1'])
  174. def aliases_cmd1(self):
  175. return "Inside Aliases.aliases_cmd1"
  176. class ArgparseExtTestCase(test.CementExtTestCase):
  177. def setUp(self):
  178. super(ArgparseExtTestCase, self).setUp()
  179. self.app = self.make_app(APP,
  180. argument_handler=ArgparseArgumentHandler,
  181. handlers=[
  182. Sixth,
  183. Base,
  184. Second,
  185. Third,
  186. Fourth,
  187. Fifth,
  188. Seventh,
  189. ],
  190. )
  191. def test_clean_label(self):
  192. self.eq(_clean_label('some_cmd_name'), 'some-cmd-name')
  193. def test_clean_func(self):
  194. self.eq(_clean_func('some-cmd-name'), 'some_cmd_name')
  195. def test_base_default(self):
  196. if not ARGPARSE_SUPPORTS_DEFAULTS:
  197. raise test.SkipTest(
  198. 'Argparse does not support default commands in Python < 3.4'
  199. )
  200. with self.app as app:
  201. res = app.run()
  202. self.eq(res, "Inside Base.default")
  203. def test_base_cmd1(self):
  204. with self.app as app:
  205. app._meta.argv = ['cmd1']
  206. res = app.run()
  207. self.eq(res, "Inside Base.cmd1")
  208. def test_base_command_with_dashes(self):
  209. with self.app as app:
  210. app._meta.argv = ['command-with-dashes']
  211. res = app.run()
  212. self.eq(res, "Inside Base.command_with_dashes")
  213. def test_controller_commands(self):
  214. with self.app as app:
  215. app._meta.argv = ['cmd2']
  216. res = app.run()
  217. self.eq(res, "Inside Second.cmd2")
  218. self.setUp()
  219. with self.app as app:
  220. app._meta.argv = ['third', 'cmd3']
  221. res = app.run()
  222. self.eq(res, "Inside Third.cmd3")
  223. self.setUp()
  224. with self.app as app:
  225. app._meta.argv = ['third', 'cmd4']
  226. res = app.run()
  227. self.eq(res, "Inside Fourth.cmd4")
  228. self.setUp()
  229. with self.app as app:
  230. app._meta.argv = ['third', 'fifth', 'cmd5']
  231. res = app.run()
  232. self.eq(res, "Inside Fifth.cmd5")
  233. self.setUp()
  234. with self.app as app:
  235. app._meta.argv = ['third', 'fifth', 'sixth', 'cmd6']
  236. res = app.run()
  237. self.eq(res, "Inside Sixth.cmd6")
  238. self.setUp()
  239. with self.app as app:
  240. app._meta.argv = ['third', 'cmd7']
  241. res = app.run()
  242. self.eq(res, "Inside Seventh.cmd7")
  243. def test_base_cmd1_parsing(self):
  244. with self.app as app:
  245. app._meta.argv = ['--foo=bar', 'cmd1']
  246. res = app.run()
  247. self.eq(res, "Inside Base.cmd1")
  248. self.eq(app.pargs.foo, 'bar')
  249. def test_second_cmd2(self):
  250. with self.app as app:
  251. app._meta.argv = ['--foo=bar', '--foo2=bar2', 'cmd2']
  252. res = app.run()
  253. self.eq(res, "Inside Second.cmd2")
  254. self.eq(app.pargs.foo, 'bar')
  255. self.eq(app.pargs.foo2, 'bar2')
  256. def test_third_cmd3(self):
  257. with self.app as app:
  258. app._meta.argv = [
  259. '--foo=bar', '--foo2=bar2',
  260. 'third', '--foo3=bar3', '--foo4=bar4', '--foo7=bar7', 'cmd3',
  261. ]
  262. res = app.run()
  263. self.eq(res, "Inside Third.cmd3")
  264. self.eq(app.pargs.foo, 'bar')
  265. self.eq(app.pargs.foo2, 'bar2')
  266. self.eq(app.pargs.foo3, 'bar3')
  267. self.eq(app.pargs.foo4, 'bar4')
  268. self.eq(app.pargs.foo7, 'bar7')
  269. def test_fifth_cmd5(self):
  270. with self.app as app:
  271. app._meta.argv = [
  272. '--foo=bar', '--foo2=bar2',
  273. 'third', '--foo3=bar3', '--foo4=bar4',
  274. 'fifth', '--foo5=bar5', 'cmd5'
  275. ]
  276. res = app.run()
  277. self.eq(res, "Inside Fifth.cmd5")
  278. self.eq(app.pargs.foo, 'bar')
  279. self.eq(app.pargs.foo2, 'bar2')
  280. self.eq(app.pargs.foo3, 'bar3')
  281. self.eq(app.pargs.foo4, 'bar4')
  282. self.eq(app.pargs.foo5, 'bar5')
  283. def test_sixth_cmd6(self):
  284. with self.app as app:
  285. app._meta.argv = [
  286. '--foo=bar', '--foo2=bar2',
  287. 'third', '--foo3=bar3', '--foo4=bar4',
  288. 'fifth', '--foo5=bar5', 'sixth', '--foo6=bar6', 'cmd6',
  289. ]
  290. res = app.run()
  291. self.eq(res, "Inside Sixth.cmd6")
  292. self.eq(app.pargs.foo, 'bar')
  293. self.eq(app.pargs.foo2, 'bar2')
  294. self.eq(app.pargs.foo3, 'bar3')
  295. self.eq(app.pargs.foo4, 'bar4')
  296. self.eq(app.pargs.foo5, 'bar5')
  297. self.eq(app.pargs.foo6, 'bar6')
  298. def test_seventh_cmd7(self):
  299. with self.app as app:
  300. app._meta.argv = [
  301. '--foo=bar', '--foo2=bar2',
  302. 'third', '--foo3=bar3', '--foo4=bar4', '--foo7=bar7', 'cmd7',
  303. ]
  304. res = app.run()
  305. self.eq(res, "Inside Seventh.cmd7")
  306. self.eq(app.pargs.foo, 'bar')
  307. self.eq(app.pargs.foo2, 'bar2')
  308. self.eq(app.pargs.foo3, 'bar3')
  309. self.eq(app.pargs.foo4, 'bar4')
  310. self.eq(app.pargs.foo7, 'bar7')
  311. def test_collect(self):
  312. with self.app as app:
  313. args = self.app.controller._collect_arguments()
  314. cmds = self.app.controller._collect_commands()
  315. args2, cmds2 = self.app.controller._collect()
  316. self.eq((args, cmds), (args2, cmds2))
  317. def test_controller_embedded_on_base(self):
  318. self.app._meta.argv = ['cmd2']
  319. with self.app as app:
  320. res = app.run()
  321. self.eq(res, "Inside Second.cmd2")
  322. def test_controller_command_arguments(self):
  323. self.app._meta.argv = ['cmd2', '--cmd2-foo=bar2']
  324. with self.app as app:
  325. res = app.run()
  326. self.eq(res, "Inside Second.cmd2 : Foo > bar2")
  327. def test_controller_default_nested_on_base(self):
  328. if not ARGPARSE_SUPPORTS_DEFAULTS:
  329. raise test.SkipTest(
  330. 'Argparse does not support default commands in Python < 3.4'
  331. )
  332. self.app._meta.argv = ['third']
  333. with self.app as app:
  334. res = app.run()
  335. self.eq(res, "Inside Third.default")
  336. def test_controller_command_nested_on_base(self):
  337. self.app._meta.argv = ['third', 'cmd3']
  338. with self.app as app:
  339. res = app.run()
  340. self.eq(res, "Inside Third.cmd3")
  341. def test_controller_doubled_embedded(self):
  342. self.app._meta.argv = ['third', 'cmd4']
  343. with self.app as app:
  344. res = app.run()
  345. self.eq(res, "Inside Fourth.cmd4")
  346. def test_controller_default_double_nested(self):
  347. if not ARGPARSE_SUPPORTS_DEFAULTS:
  348. raise test.SkipTest(
  349. 'Argparse does not support default commands in Python < 3.4'
  350. )
  351. self.app._meta.argv = ['third', 'fifth']
  352. with self.app as app:
  353. res = app.run()
  354. self.eq(res, "Inside Fifth.default")
  355. def test_controller_command_double_nested(self):
  356. self.app._meta.argv = ['third', 'fifth', 'cmd5']
  357. with self.app as app:
  358. res = app.run()
  359. self.eq(res, "Inside Fifth.cmd5")
  360. def test_alternative_default(self):
  361. if not ARGPARSE_SUPPORTS_DEFAULTS:
  362. raise test.SkipTest(
  363. 'Argparse does not support default commands in Python < 3.4'
  364. )
  365. self.reset_backend()
  366. self.app = self.make_app(APP,
  367. argv=['alternative_default'],
  368. argument_handler=ArgparseArgumentHandler,
  369. handlers=[
  370. Base,
  371. AlternativeDefault,
  372. ],
  373. )
  374. with self.app as app:
  375. res = app.run()
  376. self.eq(res,
  377. "Inside AlternativeDefault.alternative_default")
  378. @test.raises(FrameworkError)
  379. def test_bad_alternative_default_command(self):
  380. if not ARGPARSE_SUPPORTS_DEFAULTS:
  381. raise test.SkipTest(
  382. 'Argparse does not support default commands in Python < 3.4'
  383. )
  384. self.reset_backend()
  385. self.app = self.make_app(APP,
  386. argv=['bad_alternative_default'],
  387. argument_handler=ArgparseArgumentHandler,
  388. handlers=[
  389. Base,
  390. BadAlternativeDefault,
  391. ],
  392. )
  393. try:
  394. with self.app as app:
  395. res = app.run()
  396. except FrameworkError as e:
  397. res = re.match("(.*)does not exist(.*)bogus_default(.*)",
  398. e.__str__())
  399. self.ok(res)
  400. raise
  401. @test.raises(InterfaceError)
  402. def test_invalid_stacked_on(self):
  403. self.reset_backend()
  404. try:
  405. self.app = self.make_app(APP,
  406. argument_handler=ArgparseArgumentHandler,
  407. handlers=[
  408. Base,
  409. Unstacked,
  410. ],
  411. )
  412. with self.app as app:
  413. res = app.run()
  414. except InterfaceError as e:
  415. self.ok(re.match("(.*)is not stacked anywhere!(.*)", e.msg))
  416. raise
  417. @test.raises(InterfaceError)
  418. def test_invalid_stacked_type(self):
  419. self.reset_backend()
  420. try:
  421. self.app = self.make_app(APP,
  422. argument_handler=ArgparseArgumentHandler,
  423. handlers=[
  424. Base,
  425. BadStackType,
  426. ],
  427. )
  428. with self.app as app:
  429. res = app.run()
  430. except InterfaceError as e:
  431. self.ok(re.match("(.*)has an unknown stacked type(.*)", e.msg))
  432. raise
  433. @test.raises(ArgumentError)
  434. def test_duplicate_arguments(self):
  435. self.reset_backend()
  436. try:
  437. self.app = self.make_app(APP,
  438. argument_handler=ArgparseArgumentHandler,
  439. handlers=[
  440. Base,
  441. DuplicateArguments,
  442. ],
  443. )
  444. with self.app as app:
  445. res = app.run()
  446. except ArgumentError as e:
  447. self.ok(re.match("(.*)conflicting option string(.*)",
  448. e.__str__()))
  449. raise
  450. @test.raises(ArgumentError)
  451. def test_controller_command_duplicate_arguments(self):
  452. self.reset_backend()
  453. try:
  454. self.app = self.make_app(APP,
  455. argument_handler=ArgparseArgumentHandler,
  456. handlers=[
  457. Base,
  458. ControllerCommandDuplicateArguments,
  459. ],
  460. )
  461. with self.app as app:
  462. res = app.run()
  463. except ArgumentError as e:
  464. self.ok(re.match("(.*)conflicting option string(.*)",
  465. e.__str__()))
  466. raise
  467. def test_aliases(self):
  468. if sys.version_info[0] < 3:
  469. raise test.SkipTest(
  470. 'Argparse does not support aliases in Python < 3'
  471. )
  472. self.reset_backend()
  473. self.app = self.make_app(APP,
  474. argument_handler=ArgparseArgumentHandler,
  475. handlers=[
  476. Base,
  477. Aliases,
  478. ],
  479. )
  480. with self.app as app:
  481. app._meta.argv = ['aliases', 'aliases-cmd1']
  482. res = app.run()
  483. self.eq(res, "Inside Aliases.aliases_cmd1")
  484. app._meta.argv = ['aliases', 'aliases-cmd-1']
  485. app._setup_arg_handler()
  486. res = app.run()
  487. self.eq(res, "Inside Aliases.aliases_cmd1")
  488. app._meta.argv = ['aliases-controller', 'aliases-cmd1']
  489. app._setup_arg_handler()
  490. res = app.run()
  491. self.eq(res, "Inside Aliases.aliases_cmd1")
  492. app._meta.argv = ['ac', 'ac1']
  493. app._setup_arg_handler()
  494. res = app.run()
  495. self.eq(res, "Inside Aliases.aliases_cmd1")