Browse Source

opts: allow combined short option with parameter; add tests

`foo -abparam`, equivalent to `foo -a -bparam`, is now allowed, in conformity
with standard Unix option parsing convention.
The MMGen Project 1 month ago
parent
commit
6e728b7f4d
4 changed files with 39 additions and 15 deletions
  1. 1 1
      mmgen/data/version
  2. 3 5
      mmgen/opts.py
  3. 33 9
      test/cmdtest_py_d/ct_opts.py
  4. 2 0
      test/misc/opts_main.py

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-15.1.dev3
+15.1.dev4

+ 3 - 5
mmgen/opts.py

@@ -69,13 +69,11 @@ def process_uopts(opts_data, opts):
 					opt, parm = arg[2:].split('=', 1) if '=' in arg else (arg[2:], None)
 					die('CmdlineOptError', f'--{opt}: unrecognized option')
 			elif arg[0] == '-' and len(arg) > 1:
-				for j, sopt in enumerate(arg[1:]):
+				for j, sopt in enumerate(arg[1:], 2):
 					if sopt in opts:
 						if opts[sopt].has_parm:
-							if j > 0:
-								die('CmdlineOptError', f'{arg}: short option with parameters cannot be combined')
-							if arg[2:]:
-								yield (opts[sopt].name, arg[2:])
+							if arg[j:]:
+								yield (opts[sopt].name, arg[j:])
 							else:
 								idx += 1
 								if idx == argv_len or (parm := sys.argv[idx]).startswith('-'):

+ 33 - 9
test/cmdtest_py_d/ct_opts.py

@@ -33,6 +33,14 @@ class CmdTestOpts(CmdTestBase):
 		('opt_good9',            (41, 'good cmdline arg ‘-’', [])),
 		('opt_good10',           (41, 'good cmdline arg ‘-’ with arg', [])),
 		('opt_good11',           (41, 'good cmdline arg ‘-’ with option', [])),
+		('opt_good12',           (41, 'good cmdline opt (short opt with option)', [])),
+		('opt_good13',           (41, 'good cmdline opt (short opt with option)', [])),
+		('opt_good14',           (41, 'good cmdline opt (combined short opt with option)', [])),
+		('opt_good15',           (41, 'good cmdline opt (combined short opt with option)', [])),
+		('opt_good16',           (41, 'good cmdline opt (param with equals signs)', [])),
+		('opt_good17',           (41, 'good cmdline opt (param with equals signs)', [])),
+		('opt_good18',           (41, 'good cmdline opt (param with equals signs)', [])),
+		('opt_good19',           (41, 'good cmdline opt (param with equals signs)', [])),
 		('opt_bad_param',        (41, 'bad global opt (--pager=1)', [])),
 		('opt_bad_infile',       (41, 'bad infile parameter', [])),
 		('opt_bad_outdir',       (41, 'bad outdir parameter', [])),
@@ -40,8 +48,6 @@ class CmdTestOpts(CmdTestBase):
 		('opt_bad_autoset',      (41, 'invalid autoset value', [])),
 		('opt_invalid_1',     (41, 'invalid cmdline opt ‘--x’', [])),
 		('opt_invalid_2',     (41, 'invalid cmdline opt ‘---’', [])),
-		('opt_invalid_3',     (41, 'invalid cmdline opt (combined short opt with param)', [])),
-		('opt_invalid_4',     (41, 'invalid cmdline opt (combined short opt with param)', [])),
 		('opt_invalid_5',     (41, 'invalid cmdline opt (missing parameter)', [])),
 		('opt_invalid_6',     (41, 'invalid cmdline opt (missing parameter)', [])),
 		('opt_invalid_7',     (41, 'invalid cmdline opt (parameter not required)', [])),
@@ -178,6 +184,30 @@ class CmdTestOpts(CmdTestBase):
 	def opt_good11(self):
 		return self.check_vals(['-q', '-', '-x'], (('arg1', '-'), ('arg2', '-x')))
 
+	def opt_good12(self):
+		return self.check_vals(['-l128'], (('cfg.seed_len', '128'),))
+
+	def opt_good13(self):
+		return self.check_vals(['-l', '128'], (('cfg.seed_len', '128'),))
+
+	def opt_good14(self):
+		return self.check_vals(['-kl128'], (('cfg.keep_label', 'True'), ('cfg.seed_len', '128')))
+
+	def opt_good15(self):
+		return self.check_vals(['-kl', '128'], (('cfg.keep_label', 'True'), ('cfg.seed_len', '128')))
+
+	def opt_good16(self):
+		return self.check_vals(['--point=x=1,y=2,z=3'], (('cfg.point', 'x=1,y=2,z=3'),))
+
+	def opt_good17(self):
+		return self.check_vals(['--point', 'x=1,y=2,z=3'], (('cfg.point', 'x=1,y=2,z=3'),))
+
+	def opt_good18(self):
+		return self.check_vals(['-xx=1,y=2,z=3'], (('cfg.point', 'x=1,y=2,z=3'),))
+
+	def opt_good19(self):
+		return self.check_vals(['-x', 'x=1,y=2,z=3'], (('cfg.point', 'x=1,y=2,z=3'),))
+
 	def opt_bad_param(self):
 		return self.do_run(['--pager=1'], 'no parameter', 1)
 
@@ -206,12 +236,6 @@ class CmdTestOpts(CmdTestBase):
 	def opt_invalid_2(self):
 		return self.opt_invalid(['---'], 'must be at least', 1)
 
-	def opt_invalid_3(self):
-		return self.opt_invalid(['-kl3'], 'short option with parameters', 1)
-
-	def opt_invalid_4(self):
-		return self.opt_invalid(['-kl 3'], 'short option with parameters', 1)
-
 	def opt_invalid_5(self):
 		return self.opt_invalid(['-l'], 'missing parameter', 1)
 
@@ -222,7 +246,7 @@ class CmdTestOpts(CmdTestBase):
 		return self.opt_invalid(['--quiet=1'], 'requires no parameter', 1)
 
 	def opt_invalid_8(self):
-		return self.opt_invalid(['-x'], 'unrecognized option', 1)
+		return self.opt_invalid(['-w'], 'unrecognized option', 1)
 
 	def opt_invalid_9(self):
 		return self.opt_invalid(['--frobnicate'], 'unrecognized option', 1)

+ 2 - 0
test/misc/opts_main.py

@@ -27,6 +27,7 @@ opts_data = {
 -q, --quiet           Be quieter
 -t, --min-temp=    t  Minimum temperature (in degrees Celsius)
 -T, --max-temp=    t  Maximum temperature (in degrees Celsius)
+-x, --point=       P  Point in Euclidean space
 -X, --cached-balances Use cached balances (Ethereum only)
 -v, --verbose         Be more verbose
                       sample help_note: {kgs}
@@ -67,6 +68,7 @@ for k in (
 	'max_temp',
 	'coin',
 	'pager',
+	'point',
 	):
 	msg('{:30} {}'.format( f'cfg.{k}:', getattr(cfg,k) ))