Skip to content

Commit

Permalink
add WString support
Browse files Browse the repository at this point in the history
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
  • Loading branch information
dirk-thomas committed Apr 30, 2019
1 parent 1918803 commit 8dfac2e
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 4 deletions.
1 change: 1 addition & 0 deletions rosidl_generator_py/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ if(BUILD_TESTING)
#"msg/Uint64.msg"
#"msg/Uint8.msg"
"msg/Various.msg"
"msg/WStrings.msg"
)

include(cmake/register_py.cmake)
Expand Down
75 changes: 75 additions & 0 deletions rosidl_generator_py/resource/_msg_support.c.em
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ from rosidl_parser.definition import NamespacedType
def primitive_msg_type_to_c(type_):
from rosidl_generator_c import BASIC_IDL_TYPES_TO_C
from rosidl_parser.definition import AbstractString
from rosidl_parser.definition import AbstractWString
from rosidl_parser.definition import BasicType
if isinstance(type_, AbstractString):
return 'rosidl_generator_c__String'
if isinstance(type_, AbstractWString):
return 'rosidl_generator_c__U16String'
assert isinstance(type_, BasicType)
return BASIC_IDL_TYPES_TO_C[type_.typename]

Expand Down Expand Up @@ -262,6 +265,13 @@ nested_type = '__'.join(type_.namespaced_name())
Py_DECREF(field);
return false;
}
@[ elif isinstance(member.type.value_type, AbstractWString)]@
if (!rosidl_generator_c__U16String__Sequence__init(&(ros_message->@(member.name)), size)) {
PyErr_SetString(PyExc_RuntimeError, "unable to create U16String__Sequence ros_message");
Py_DECREF(seq_field);
Py_DECREF(field);
return false;
}
@[ else]@
if (!rosidl_generator_c__@(member.type.value_type.typename)__Sequence__init(&(ros_message->@(member.name)), size)) {
PyErr_SetString(PyExc_RuntimeError, "unable to create @(member.type.value_type.typename)__Sequence ros_message");
Expand Down Expand Up @@ -309,6 +319,31 @@ nested_type = '__'.join(type_.namespaced_name())
}
rosidl_generator_c__String__assign(&dest[i], PyBytes_AS_STRING(encoded_item));
Py_DECREF(encoded_item);
@[ elif isinstance(member.type.value_type, AbstractWString)]@
assert(PyUnicode_Check(item));
// the returned string starts with a BOM mark
PyObject * encoded_item = PyUnicode_AsUTF16String(item);
if (!encoded_item) {
Py_DECREF(seq_field);
Py_DECREF(field);
return false;
}
char * buffer;
Py_ssize_t length;
int rc = PyBytes_AsStringAndSize(encoded_item, &buffer, &length);
if (rc) {
Py_DECREF(seq_field);
Py_DECREF(field);
return false;
}
// use offset of 2 to skip BOM mark
bool succeeded = rosidl_generator_c__U16String__assignn_from_char(&dest[i], buffer + 2, length - 2);
if (!succeeded) {
Py_DECREF(seq_field);
Py_DECREF(field);
return false;
}
Py_DECREF(encoded_item);
@[ elif isinstance(member.type.value_type, BasicType) and member.type.value_type.typename == 'boolean']@
assert(PyBool_Check(item));
@primitive_msg_type_to_c(member.type.value_type) tmp = (item == Py_True);
Expand Down Expand Up @@ -370,6 +405,28 @@ nested_type = '__'.join(type_.namespaced_name())
}
rosidl_generator_c__String__assign(&ros_message->@(member.name), PyBytes_AS_STRING(encoded_field));
Py_DECREF(encoded_field);
@[ elif isinstance(member.type, AbstractWString)]@
assert(PyUnicode_Check(field));
// the returned string starts with a BOM mark
PyObject * encoded_field = PyUnicode_AsUTF16String(field);
if (!encoded_field) {
Py_DECREF(field);
return false;
}
char * buffer;
Py_ssize_t length;
int rc = PyBytes_AsStringAndSize(encoded_field, &buffer, &length);
if (rc) {
Py_DECREF(field);
return false;
}
// use offset of 2 to skip BOM mark
bool succeeded = rosidl_generator_c__U16String__assignn_from_char(&ros_message->@(member.name), buffer + 2, length - 2);
if (!succeeded) {
Py_DECREF(field);
return false;
}
Py_DECREF(encoded_field);
@[ elif isinstance(member.type, BasicType) and member.type.typename == 'boolean']@
assert(PyBool_Check(field));
ros_message->@(member.name) = (Py_True == field);
Expand Down Expand Up @@ -560,6 +617,15 @@ nested_type = '__'.join(type_.namespaced_name())
int rc = PyList_SetItem(field, i, decoded_item);
(void)rc;
assert(rc == 0);
@[ elif isinstance(member.type.value_type, AbstractWString)]@
int byteorder = 0;
PyObject * decoded_item = PyUnicode_DecodeUTF16((const char *)src[i].data, src[i].size * sizeof(uint16_t), NULL, &byteorder);
if (!decoded_item) {
return NULL;
}
int rc = PyList_SetItem(field, i, decoded_item);
(void)rc;
assert(rc == 0);
@[ elif isinstance(member.type.value_type, BasicType) and member.type.value_type.typename == 'boolean']@
@# using PyBool_FromLong because PyList_SetItem will steal ownership of the passed item
int rc = PyList_SetItem(field, i, PyBool_FromLong(src[i] ? 1 : 0));
Expand Down Expand Up @@ -614,6 +680,15 @@ nested_type = '__'.join(type_.namespaced_name())
if (!field) {
return NULL;
}
@[ elif isinstance(member.type, AbstractWString)]@
int byteorder = 0;
field = PyUnicode_DecodeUTF16(
(const char *)ros_message->@(member.name).data,
ros_message->@(member.name).size * sizeof(uint16_t),
NULL, &byteorder);
if (!field) {
return NULL;
}
@[ elif isinstance(member.type, BasicType) and member.type.typename == 'boolean']@
@# using PyBool_FromLong allows treating the variable uniformly by calling Py_DECREF on it later
field = PyBool_FromLong(ros_message->@(member.name) ? 1 : 0);
Expand Down
8 changes: 4 additions & 4 deletions rosidl_generator_py/rosidl_generator_py/generate_py_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
from rosidl_cmake import generate_files
from rosidl_cmake import get_newest_modification_time
from rosidl_cmake import read_generator_arguments
from rosidl_parser.definition import AbstractGenericString
from rosidl_parser.definition import AbstractNestedType
from rosidl_parser.definition import AbstractSequence
from rosidl_parser.definition import AbstractString
from rosidl_parser.definition import Array
from rosidl_parser.definition import BasicType
from rosidl_parser.definition import CHARACTER_TYPES
Expand Down Expand Up @@ -148,7 +148,7 @@ def value_to_py(type_, value, array_as_tuple=False):
def primitive_value_to_py(type_, value):
assert value is not None

if isinstance(type_, AbstractString):
if isinstance(type_, AbstractGenericString):
return quoted_string(value)

assert isinstance(type_, BasicType)
Expand Down Expand Up @@ -190,7 +190,7 @@ def constant_value_to_py(type_, value):
if type_.typename in FLOATING_POINT_TYPES:
return '%s' % value

if isinstance(type_, AbstractString):
if isinstance(type_, AbstractGenericString):
return quoted_string(value)

assert False, "unknown constant type '%s'" % type_
Expand All @@ -212,7 +212,7 @@ def get_python_type(type_):
if isinstance(type_, NamespacedType):
return type_.name

if isinstance(type_, AbstractString):
if isinstance(type_, AbstractGenericString):
return 'str'

if isinstance(type_, AbstractNestedType):
Expand Down
24 changes: 24 additions & 0 deletions rosidl_generator_py/test/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from rosidl_generator_py.msg import StringArrays
from rosidl_generator_py.msg import Strings
from rosidl_generator_py.msg import Various
from rosidl_generator_py.msg import WStrings


def test_strings():
Expand All @@ -31,6 +32,13 @@ def test_strings():
assert 'Hello world!' == a.def_string


def test_wstrings():
a = WStrings()

assert '' == a.empty_wstring
assert 'Hello world!' == a.def_wstring


def test_arrays_of_bounded_strings():
a = StringArrays()
array_valid_string_length = ['a' * 2, 'b' * 3, 'c' * 4]
Expand Down Expand Up @@ -123,6 +131,22 @@ def test_default_values():
with pytest.raises(AttributeError):
setattr(Strings, 'DEF_STRING__DEFAULT', 'bar')

a = WStrings()

assert '' == a.empty_wstring
assert 'Hello world!' == a.def_wstring
a.def_wstring = 'Bye world'
assert 'Bye world' == a.def_wstring
assert 'Hello world!' == WStrings.DEF_WSTRING__DEFAULT
assert 'Hello world!' == a.DEF_WSTRING__DEFAULT

assert "Hello'world!" == a.DEF_WSTRING2__DEFAULT
assert 'Hello"world!' == a.DEF_WSTRING3__DEFAULT
assert "Hello'world!" == a.DEF_WSTRING4__DEFAULT
assert 'Hello"world!' == a.DEF_WSTRING5__DEFAULT
with pytest.raises(AttributeError):
setattr(WStrings, 'DEF_WSTRING__DEFAULT', 'bar')

b = StringArrays()
assert ['What', 'a', 'wonderful', 'world', '!'] == b.DEF_STRING_DYNAMIC_ARRAY_VALUE__DEFAULT
assert ['Hello', 'World', '!'] == b.DEF_STRING_STATIC_ARRAY_VALUE__DEFAULT
Expand Down

0 comments on commit 8dfac2e

Please sign in to comment.