Browse Source

fix setting of autoset opts in cfg file

The MMGen Project 2 years ago
parent
commit
29e6822bd1
5 changed files with 77 additions and 37 deletions
  1. 1 1
      mmgen/data/release_date
  2. 1 1
      mmgen/data/version
  3. 45 35
      mmgen/opts.py
  4. 4 0
      test/misc/cfg.py
  5. 26 0
      test/test_py_d/ts_cfg.py

+ 1 - 1
mmgen/data/release_date

@@ -1 +1 @@
-December 2022
+January 2023

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-13.3.dev30
+13.3.dev31

+ 45 - 35
mmgen/opts.py

@@ -161,7 +161,7 @@ def set_for_type(val,refval,desc,invert_bool=False,src=None):
 		' in {!r}'.format(src) if src else '',
 		' in {!r}'.format(src) if src else '',
 		type(refval).__name__) )
 		type(refval).__name__) )
 
 
-def override_globals_from_cfg_file(ucfg,need_proto):
+def override_globals_from_cfg_file(ucfg,autoset_opts,need_proto):
 	if need_proto:
 	if need_proto:
 		from .protocol import init_proto
 		from .protocol import init_proto
 	for d in ucfg.get_lines():
 	for d in ucfg.get_lines():
@@ -186,6 +186,8 @@ def override_globals_from_cfg_file(ucfg,need_proto):
 				die( 'CfgFileParseError', f'Parse error in file {ucfg.fn!r}, line {d.lineno}' )
 				die( 'CfgFileParseError', f'Parse error in file {ucfg.fn!r}, line {d.lineno}' )
 			val_conv = set_for_type(val,refval,attr,src=ucfg.fn)
 			val_conv = set_for_type(val,refval,attr,src=ucfg.fn)
 			setattr(cls,attr,val_conv)
 			setattr(cls,attr,val_conv)
+		elif d.name in g.autoset_opts:
+			autoset_opts[d.name] = d.value
 		else:
 		else:
 			from .util import die
 			from .util import die
 			die( 'CfgFileParseError', f'{d.name!r}: unrecognized option in {ucfg.fn!r}, line {d.lineno}' )
 			die( 'CfgFileParseError', f'{d.name!r}: unrecognized option in {ucfg.fn!r}, line {d.lineno}' )
@@ -356,13 +358,15 @@ def init(
 	from .util import wrap_ripemd160
 	from .util import wrap_ripemd160
 	wrap_ripemd160() # ripemd160 used by cfg_file()
 	wrap_ripemd160() # ripemd160 used by cfg_file()
 
 
+	cfgfile_autoset_opts = {}
+
 	if not (opt.skip_cfg_file or opt.bob or opt.alice or g.prog_name == 'mmgen-regtest'):
 	if not (opt.skip_cfg_file or opt.bob or opt.alice or g.prog_name == 'mmgen-regtest'):
 		from .cfg import cfg_file
 		from .cfg import cfg_file
 		# check for changes in system template file - term must be initialized
 		# check for changes in system template file - term must be initialized
 		# workaround: g.test_suite is needed by cfg.py, but g is not set from environment yet
 		# workaround: g.test_suite is needed by cfg.py, but g is not set from environment yet
 		g.test_suite = False if os.getenv('MMGEN_TEST_SUITE') in (None,'','false','0') else True
 		g.test_suite = False if os.getenv('MMGEN_TEST_SUITE') in (None,'','false','0') else True
 		cfg_file('sample')
 		cfg_file('sample')
-		override_globals_from_cfg_file( cfg_file('usr'), need_proto )
+		override_globals_from_cfg_file( cfg_file('usr'), cfgfile_autoset_opts, need_proto )
 
 
 	override_globals_and_set_opts_from_env(opt)
 	override_globals_and_set_opts_from_env(opt)
 
 
@@ -421,8 +425,15 @@ def init(
 	# Check user-set opts without modifying them
 	# Check user-set opts without modifying them
 	check_usr_opts(po.user_opts)
 	check_usr_opts(po.user_opts)
 
 
-	# Check all opts against g.autoset_opts, setting if unset
-	check_and_set_autoset_opts()
+	# Check autoset opts, setting if unset
+	for key in g.autoset_opts:
+		if hasattr(opt,key):
+			if getattr(opt,key) is not None:
+				setattr(opt, key, get_autoset_opt(key,getattr(opt,key),src='cmdline'))
+			elif key in cfgfile_autoset_opts:
+				setattr(opt, key, get_autoset_opt(key,cfgfile_autoset_opts[key],src='cfgfile'))
+			else:
+				setattr(opt, key, g.autoset_opts[key].choices[0])
 
 
 	set_auto_typeset_opts()
 	set_auto_typeset_opts()
 
 
@@ -662,37 +673,36 @@ def set_auto_typeset_opts():
 			if val is not None: # typeset only if opt is set
 			if val is not None: # typeset only if opt is set
 				setattr(opt,key,ref_type(val))
 				setattr(opt,key,ref_type(val))
 
 
-def check_and_set_autoset_opts(): # Raises exception if any check fails
+def get_autoset_opt(key,val,src):
+
+	def die_on_err(desc):
+		from .util import fmt_list,die
+		die(
+			'UserOptError',
+			'{a!r}: invalid {b} (not {c}: {d})'.format(
+				a = val,
+				b = {
+					'cmdline': 'parameter for option --{}'.format(key.replace('_','-')),
+					'cfgfile': 'value for cfg file option {!r}'.format(key)
+				}[src],
+				c = desc,
+				d = fmt_list(data.choices) ))
+
+	class opt_type:
+
+		def nocase_str():
+			if val.lower() in data.choices:
+				return val.lower()
+			else:
+				die_on_err('one of')
 
 
-	def nocase_str(key,val,asd):
-		try:
-			return asd.choices.index(val)
-		except:
-			return 'one of'
+		def nocase_pfx():
+			cs = [s for s in data.choices if s.startswith(val.lower())]
+			if len(cs) == 1:
+				return cs[0]
+			else:
+				die_on_err('unique substring of')
 
 
-	def nocase_pfx(key,val,asd):
-		cs = [s.startswith(val.lower()) for s in asd.choices]
-		if cs.count(True) == 1:
-			return cs.index(True)
-		else:
-			return 'unique substring of'
+	data = g.autoset_opts[key]
 
 
-	for key,asd in g.autoset_opts.items():
-		if hasattr(opt,key):
-			val = getattr(opt,key)
-			if val is None:
-				setattr(opt,key,asd.choices[0])
-			else:
-				ret = locals()[asd.type](key,val,asd)
-				if type(ret) is str:
-					from .util import fmt_list,die
-					die( 'UserOptError',
-						'{!r}: invalid parameter for option --{} (not {}: {})'.format(
-							val,
-							key.replace('_','-'),
-							ret,
-							fmt_list(asd.choices) ))
-				elif ret is True:
-					setattr(opt,key,val)
-				else:
-					setattr(opt,key,asd.choices[ret])
+	return getattr(opt_type,data.type)()

+ 4 - 0
test/misc/cfg.py

@@ -29,5 +29,9 @@ if cmd_args:
 				varname,
 				varname,
 				getattr(proto,varname)
 				getattr(proto,varname)
 			))
 			))
+	elif cmd_args[0] == 'autoset_opts':
+		assert opt.rpc_backend == 'aiohttp', "opt.rpc_backend != 'aiohttp'"
+	elif cmd_args[0] == 'autoset_opts_cmdline':
+		assert opt.rpc_backend == 'curl', "opt.rpc_backend != 'curl'"
 	elif cmd_args[0] == 'mnemonic_entry_modes':
 	elif cmd_args[0] == 'mnemonic_entry_modes':
 		msg( 'mnemonic_entry_modes: {}'.format(g.mnemonic_entry_modes) )
 		msg( 'mnemonic_entry_modes: {}'.format(g.mnemonic_entry_modes) )

+ 26 - 0
test/test_py_d/ts_cfg.py

@@ -29,6 +29,10 @@ class TestSuiteCfg(TestSuiteBase):
 		('altered_sample',           (40,'init with user-modified cfg sample file', [])),
 		('altered_sample',           (40,'init with user-modified cfg sample file', [])),
 		('old_sample',               (40,'init with old v2 cfg sample file', [])),
 		('old_sample',               (40,'init with old v2 cfg sample file', [])),
 		('old_sample_bad_var',       (40,'init with old v2 cfg sample file and bad variable in mmgen.cfg', [])),
 		('old_sample_bad_var',       (40,'init with old v2 cfg sample file and bad variable in mmgen.cfg', [])),
+		('autoset_opts',             (40,'setting autoset opts', [])),
+		('autoset_opts_cmdline',     (40,'setting autoset opts (override on cmdline)', [])),
+		('autoset_opts_bad',         (40,'setting autoset opts (bad value in cfg file)', [])),
+		('autoset_opts_bad_cmdline', (40,'setting autoset opts (bad param on cmdline)', [])),
 		('coin_specific_vars',       (40,'setting coin-specific vars', [])),
 		('coin_specific_vars',       (40,'setting coin-specific vars', [])),
 		('chain_names',              (40,'setting chain names', [])),
 		('chain_names',              (40,'setting chain names', [])),
 		('mnemonic_entry_modes',     (40,'setting mnemonic entry modes', [])),
 		('mnemonic_entry_modes',     (40,'setting mnemonic entry modes', [])),
@@ -156,6 +160,28 @@ class TestSuiteCfg(TestSuiteBase):
 			old_set       = True,
 			old_set       = True,
 			pexpect_spawn = False if g.platform == 'win' else True )
 			pexpect_spawn = False if g.platform == 'win' else True )
 
 
+	def _autoset_opts(self,args=[],text='rpc_backend aiohttp\n'):
+		write_to_file( self.path('usr'), text )
+		imsg(yellow(f'Wrote cfg file:\n  {text}'))
+		return self.spawn_test(args=args)
+
+	def autoset_opts(self):
+		return self._autoset_opts(args=['autoset_opts'])
+
+	def autoset_opts_cmdline(self):
+		return self._autoset_opts(args=['--rpc-backend=curl','autoset_opts_cmdline'])
+
+	def _autoset_opts_bad(self,kwargs):
+		t = self._autoset_opts(**kwargs)
+		t.req_exit_val = 1
+		return t
+
+	def autoset_opts_bad(self):
+		return self._autoset_opts_bad({'text':'rpc_backend foo\n'})
+
+	def autoset_opts_bad_cmdline(self):
+		return self._autoset_opts_bad({'args':['--rpc-backend=foo']})
+
 	def coin_specific_vars(self):
 	def coin_specific_vars(self):
 		"""
 		"""
 		ensure that derived classes explicitly set these variables
 		ensure that derived classes explicitly set these variables