add more tests and fixes for command inheritance

- fix CommandType.__repr__
- secop/modules.py: command properties are allowed to be configured:
   - section 2: remove comment and rename
   - section 3: all accessible properties should be checked
- command description should be inherited also when taken from docstring
- move test for command inheritance to test_modules.py
- added tests to check for valid properties of commands

Change-Id: I5fd04e03be1faec5e54fed9735620bc5dc0f89c0
This commit is contained in:
2021-11-12 17:07:11 +01:00
parent 9058ef054b
commit 1d9e07edb4
5 changed files with 157 additions and 24 deletions

View File

@ -955,10 +955,9 @@ class CommandType(DataType):
return props
def __repr__(self):
argstr = repr(self.argument) if self.argument else ''
if self.result is None:
return 'CommandType(%s)' % argstr
return 'CommandType(%s, %s)' % (argstr, repr(self.result))
return 'CommandType(%s)' % (repr(self.argument) if self.argument else '')
return 'CommandType(%s, %s)' % (repr(self.argument), repr(self.result))
def __call__(self, value):
"""return the validated argument value or raise"""

View File

@ -318,26 +318,23 @@ class Module(HasAccessibles):
# 2) check and apply parameter_properties
# specified as '<paramname>.<propertyname> = <propertyvalue>'
# this may also be done on commands: e.g. 'stop.visibility = advanced'
for k, v in list(cfgdict.items()): # keep list() as dict may change during iter
if '.' in k[1:]:
paramname, propname = k.split('.', 1)
aname, propname = k.split('.', 1)
propvalue = cfgdict.pop(k)
paramobj = self.accessibles.get(paramname, None)
# paramobj might also be a command (not sure if this is needed)
if paramobj:
# no longer needed, this conversion is done by DataTypeType.__call__:
# if propname == 'datatype':
# propvalue = get_datatype(propvalue, k)
aobj = self.accessibles.get(aname, None)
if aobj:
try:
paramobj.setProperty(propname, propvalue)
aobj.setProperty(propname, propvalue)
except KeyError:
errors.append("'%s.%s' does not exist" %
(paramname, propname))
(aname, propname))
except BadValueError as e:
errors.append('%s.%s: %s' %
(paramname, propname, str(e)))
(aname, propname, str(e)))
else:
errors.append('%r not found' % paramname)
errors.append('%r not found' % aname)
# 3) check config for problems:
# only accept remaining config items specified in parameters
@ -423,11 +420,11 @@ class Module(HasAccessibles):
self.checkProperties()
except ConfigError as e:
errors.append(str(e))
for pname, p in self.parameters.items():
for aname, aobj in self.accessibles.items():
try:
p.checkProperties()
except ConfigError as e:
errors.append('%s: %s' % (pname, e))
aobj.checkProperties()
except (ConfigError, ProgrammingError) as e:
errors.append('%s: %s' % (aname, e))
if errors:
raise ConfigError(errors)

View File

@ -346,6 +346,9 @@ class Command(Accessible):
def __init__(self, argument=False, *, result=None, inherit=True, **kwds):
super().__init__()
if 'datatype' in kwds:
# self.init will complain about invalid keywords except 'datatype', as this is a property
raise ProgrammingError("Command() got an invalid keyword 'datatype'")
self.init(kwds)
if result or kwds or isinstance(argument, DataType) or not callable(argument):
# normal case
@ -362,7 +365,7 @@ class Command(Accessible):
self.func = argument # this is the wrapped method!
if argument.__doc__:
self.description = inspect.cleandoc(argument.__doc__)
self.name = self.func.__name__
self.name = self.func.__name__ # this is probably not needed
self._inherit = inherit # save for __set_name__
def __set_name__(self, owner, name):
@ -397,6 +400,7 @@ class Command(Accessible):
"""called when used as decorator"""
if 'description' not in self.propertyValues and func.__doc__:
self.description = inspect.cleandoc(func.__doc__)
self.ownProperties['description'] = self.description
self.func = func
return self