Skip to content
Open
1 change: 1 addition & 0 deletions houston/ardu/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class ArmDisarm(Command):
Idle: if the conditions above cannot be met, the robot will ignore the
command.
"""
uid = 'ardu:common:arm'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably better idea to add this property for all commands in the same PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely. I was expecting the CI to spot any commands with missing properties, but maybe it's missed some?

name = 'arm'
parameters = [
Parameter('arm', DiscreteValueRange([True, False]))
Expand Down
42 changes: 23 additions & 19 deletions houston/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,28 @@ def __new__(mcl,
raise TypeError(tpl_err.format(msg))
logger.debug("obtained command name: %s", name_command)

# obtain UID
logger.debug("obtaining command UID")
if 'uid' not in ns:
msg = "missing 'uid' field in Command definition"
raise TypeError(tpl_err.format(msg))

uid = ns['uid']
if not isinstance(uid, str):
t = type(uid)
msg = "expected 'uid' field to be str but was {}".format(t)
raise TypeError(tpl_err.format(msg))
logger.debug("using provided UID: %s", uid)

# ensure uid is not an empty string
if uid == '':
msg = "'uid' field must not be an empty string"
raise TypeError(tpl_err.format(msg))

# FIXME convert uid to a read-only attribute

logger.debug("obtained command UID: %s", uid)

# ensure "to_message" is implemented
if 'to_message' not in ns:
msg = "missing 'to_message' method in Command definition"
Expand Down Expand Up @@ -142,26 +164,8 @@ def __init__(cls, cls_name: str, bases, ns: Dict[str, Any]):
tpl_err = "failed to build definition for command [{}]: "
tpl_err = tpl_err.format(cls_name) + "{}"

# obtain or generate a unique identifier
if 'uid' in ns:
uid = ns['uid']
if not isinstance(uid, str):
t = type(uid)
msg = "expected 'uid' field to be str but was {}".format(t)
raise TypeError(tpl_err.format(msg))
logger.debug("using provided UID: %s", uid)
else:
uid = '{}.{}'.format(cls.__module__, cls.__qualname__)

# ensure uid is not an empty string
if uid == '':
msg = "'uid' field must not be an empty string"
raise TypeError(tpl_err.format(msg))

# convert uid to a read-only property
ns['uid'] = property(lambda u=uid: u)

# ensure that uid isn't already in use
uid = ns['uid'] # type: str
if uid in _UID_TO_COMMAND_TYPE:
msg = "'uid' already in use [%s]".format(uid)
raise TypeError(tpl_error.format(msg))
Expand Down
8 changes: 5 additions & 3 deletions houston/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ def __new__(mcl,
else:
is_abstract = False

# construct an immutable "is_abstract" property
ns['is_abstract'] = property(lambda self, v=is_abstract: v)
# FIXME create immutable attribute
ns['is_abstract'] = is_abstract

if not is_abstract:
if 'name' not in ns:
Expand All @@ -52,7 +52,9 @@ def __new__(mcl,
if sys_name == '':
msg = "System name must be a non-empty string."
raise TypeError(msg)
ns['name'] = property(lambda self, n=sys_name: n)

# FIXME create immutable class attribute
ns['name'] = sys_name

if 'state' not in ns:
msg = "System class definition is missing 'state' property"
Expand Down
1 change: 1 addition & 0 deletions test/test_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class M1(Message):
foo = attr.ib(type=str)

class C1(Command):
uid = 'test:c1'
name = 'c1'
parameters = [
Parameter('foo', DiscreteValueRange(['ON', 'OFF']))
Expand Down
14 changes: 14 additions & 0 deletions test/test_properties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import pytest

from houston.ardu.common import ArmDisarm
from houston.ardu.copter.goto import GoTo as CopterGoTo
from houston.ardu.copter import ArduCopter


def test_command_uid():
assert CopterGoTo.uid == 'ardu:copter:goto'
assert ArmDisarm.uid == 'ardu:common:arm'


def test_system_name():
assert ArduCopter.name == 'arducopter'