Skip to content

Commit b158e20

Browse files
committed
Remove compilation warning with InstanceHandle_t (#262)
* Refs #23753. Take into account dependencies for rebuild Signed-off-by: Ricardo González Moreno <ricardo@richiware.dev> * Refs #23753. Fix warning Signed-off-by: Ricardo González Moreno <ricardo@richiware.dev> * Refs #23753. Remove swig warnings Signed-off-by: Ricardo González Moreno <ricardo@richiware.dev> * Refs #23753. Add unit tests Signed-off-by: Ricardo González Moreno <ricardo@richiware.dev> * Refs #23753. Apply suggestions Signed-off-by: Ricardo González Moreno <ricardo@richiware.dev> --------- Signed-off-by: Ricardo González Moreno <ricardo@richiware.dev> (cherry picked from commit 6ee4ca5)
1 parent 7b7a574 commit b158e20

File tree

4 files changed

+307
-37
lines changed

4 files changed

+307
-37
lines changed

fastdds_python/src/swig/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ set(${PROJECT_NAME}_MODULE
2727
set(${PROJECT_NAME}_FILES
2828
${${PROJECT_NAME}_MODULE}.i
2929
)
30+
set_property(SOURCE ${${PROJECT_NAME}_FILE} PROPERTY USE_SWIG_DEPENDENCIES TRUE)
3031

3132
SET_SOURCE_FILES_PROPERTIES(
3233
${${PROJECT_NAME}_FILES}

fastdds_python/src/swig/fastdds/rtps/common/InstanceHandle.i

Lines changed: 146 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
// limitations under the License.
1414

1515
%{
16+
#include <stdexcept>
17+
#include <vector>
18+
1619
#include "fastdds/rtps/common/InstanceHandle.h"
1720

1821
// Define a hash method in global scope for InstanceHandle_t types
@@ -26,60 +29,119 @@ long hash(const eprosima::fastrtps::rtps::InstanceHandle_t& handle)
2629
}
2730
return ret;
2831
}
29-
3032
%}
3133

3234
// SWIG does not support type conversion operators correctly unless converted to a normal method
3335
%rename(get_guid) eprosima::fastrtps::rtps::InstanceHandle_t::operator const GUID_t&;
3436

3537
%ignore eprosima::fastrtps::rtps::InstanceHandleValue_t::operator [] const;
38+
%ignore eprosima::fastrtps::rtps::InstanceHandleValue_t::operator [];
3639
%ignore eprosima::fastrtps::rtps::operator <<(std::ostream&, const InstanceHandle_t&);
3740
%ignore eprosima::fastrtps::rtps::operator >>(std::istream&, InstanceHandle_t&);
38-
%rename(read_pointer_cast) eprosima::fastrtps::rtps::InstanceHandleValue_t::operator const octet* () const;
39-
%rename(write_pointer_cast) eprosima::fastrtps::rtps::InstanceHandleValue_t::operator octet* ();
40-
41-
%typemap(in) eprosima::fastrtps::rtps::InstanceHandleValue_t*(eprosima::fastrtps::rtps::InstanceHandleValue_t temp)
42-
{
43-
if (PyTuple_Check($input))
44-
{
45-
eprosima::fastrtps::rtps::octet* buf = temp;
46-
if (!PyArg_ParseTuple($input, "BBBBBBBBBBBBBBBB",
47-
buf, buf+1, buf+2, buf+3, buf+4, buf+5, buf+6, buf+7, buf+8,
48-
buf+9, buf+10, buf+11, buf+12, buf+13, buf+14, buf+15))
41+
%ignore eprosima::fastrtps::rtps::InstanceHandleValue_t::operator const octet* () const;
42+
%ignore eprosima::fastrtps::rtps::InstanceHandleValue_t::operator octet* ();
43+
44+
%extend eprosima::fastrtps::rtps::InstanceHandleValue_t {
45+
46+
// Constructor from a sequence of 16 bytes (tuple/list/bytes/bytearray)
47+
InstanceHandleValue_t(PyObject* seq) {
48+
eprosima::fastrtps::rtps::InstanceHandleValue_t* self = new eprosima::fastrtps::rtps::InstanceHandleValue_t();
49+
SWIG_PYTHON_THREAD_BEGIN_BLOCK;
50+
51+
// Fast-path: bytes
52+
if (PyBytes_Check(seq)) {
53+
if (PyBytes_GET_SIZE(seq) == 16)
54+
{
55+
const char* b = PyBytes_AS_STRING(seq);
56+
for (int i = 0; i < 16; ++i) (*self)[i] = (uint8_t)(unsigned char)b[i];
57+
}
58+
else
59+
{
60+
delete self;
61+
self = nullptr;
62+
PyErr_SetString(PyExc_ValueError, "Expected 16 bytes");
63+
}
64+
}
65+
// Fast-path: bytearray
66+
else if (PyByteArray_Check(seq))
4967
{
50-
PyErr_SetString(PyExc_TypeError, "tuple must have 16 elements");
51-
SWIG_fail;
68+
if (PyByteArray_GET_SIZE(seq) == 16)
69+
{
70+
const char* b = PyByteArray_AS_STRING(seq);
71+
for (int i = 0; i < 16; ++i) (*self)[i] = (uint8_t)(unsigned char)b[i];
72+
}
73+
else
74+
{
75+
delete self;
76+
self = nullptr;
77+
PyErr_SetString(PyExc_ValueError, "Expected 16 bytes");
78+
}
5279
}
53-
$1 = &temp;
54-
}
55-
else
56-
{
57-
PyErr_SetString(PyExc_TypeError, "expected a tuple.");
58-
SWIG_fail;
80+
else
81+
{
82+
// Generic fallback: iterable from 16 ints 0..255
83+
PyObject* it = PyObject_GetIter(seq);
84+
size_t count {0};
85+
if (it)
86+
{
87+
PyObject* item {nullptr};
88+
while ((item = PyIter_Next(it)) && count < 16)
89+
{
90+
long val = PyLong_AsLong(item);
91+
Py_DECREF(item);
92+
if (val == -1 && PyErr_Occurred())
93+
{
94+
delete self;
95+
self = nullptr;
96+
PyErr_SetString(PyExc_TypeError, "Sequence must contain integers");
97+
break;
98+
}
99+
else if (val < 0 || val > 255)
100+
{
101+
delete self;
102+
self = nullptr;
103+
PyErr_SetString(PyExc_ValueError, "Each value must be in 0..255");
104+
break;
105+
}
106+
107+
(*self)[count] = static_cast<uint8_t>(val);
108+
++count;
109+
}
110+
Py_DECREF(it);
111+
if ((nullptr != item || count != 16) && nullptr != self)
112+
{
113+
delete self;
114+
self = nullptr;
115+
PyErr_SetString(PyExc_ValueError, "Expected 16 elements");
116+
}
117+
}
118+
else
119+
{
120+
delete self;
121+
self = nullptr;
122+
PyErr_SetString(PyExc_TypeError, "Expected a sequence of 16 integers (0..255) or 16-byte object");
123+
}
124+
}
125+
126+
SWIG_PYTHON_THREAD_END_BLOCK;
127+
128+
return self;
59129
}
60-
}
61130

62-
%typemap(out) eprosima::fastrtps::rtps::InstanceHandleValue_t*
63-
{
64-
constexpr size_t ih_size = std::tuple_size<eprosima::fastrtps::rtps::KeyHash_t>::value;
65-
PyObject* python_tuple = PyTuple_New(ih_size);
131+
size_t __len__() const { return 16; }
66132

67-
if (python_tuple)
68-
{
69-
for(size_t count = 0; count < ih_size; ++count)
70-
{
71-
PyTuple_SetItem(python_tuple, count, PyInt_FromLong((*$1)[count]));
72-
}
133+
uint8_t __getitem__(size_t i) const {
134+
if (i >= 16) throw std::out_of_range("index out of range");
135+
return $self->operator[](i);
73136
}
74137

75-
$result = python_tuple;
138+
void __setitem__(size_t i, uint8_t v) {
139+
if (i >= 16) throw std::out_of_range("index out of range");
140+
$self->operator[](i) = v;
141+
}
76142
}
77143

78-
// Template for std::vector<InstanceHandle_t>
79-
%template(InstanceHandleVector) std::vector<eprosima::fastrtps::rtps::InstanceHandle_t>;
80-
%typemap(doctype) std::vector<eprosima::fastrtps::rtps::InstanceHandle_t>"InstanceHandleVector";
81-
82-
%include "fastdds/rtps/common/InstanceHandle.h"
144+
%ignore eprosima::fastrtps::rtps::InstanceHandle_t::value;
83145

84146
// Declare the comparison operators as internal to the class
85147
%extend eprosima::fastrtps::rtps::InstanceHandle_t {
@@ -106,4 +168,51 @@ long hash(const eprosima::fastrtps::rtps::InstanceHandle_t& handle)
106168
{
107169
return hash(*$self);
108170
}
171+
172+
// Setter from sequence (tuple/list/bytes/bytearray)
173+
void from_sequence(PyObject* seq) {
174+
// Reuse the constructor to validate and copy
175+
eprosima::fastrtps::rtps::InstanceHandleValue_t* tmp = new_eprosima_fastrtps_rtps_InstanceHandleValue_t(seq);
176+
if (nullptr != tmp)
177+
{
178+
for (int i = 0; i < 16; ++i) $self->value[i] = (*tmp)[i];
179+
delete tmp; // avoid memory leak
180+
}
181+
}
182+
183+
// Getter: return a tuple of 16 ints (0..255)
184+
PyObject* to_sequence() const {
185+
SWIG_PYTHON_THREAD_BEGIN_BLOCK;
186+
187+
PyObject* python_tuple = PyTuple_New(16);
188+
189+
if (python_tuple)
190+
{
191+
for(size_t count = 0; count < 16; ++count)
192+
{
193+
PyTuple_SetItem(python_tuple, count, PyInt_FromLong($self->value[count]));
194+
}
195+
}
196+
197+
SWIG_PYTHON_THREAD_END_BLOCK;
198+
199+
return python_tuple;
200+
}
109201
}
202+
203+
// Template for std::vector<InstanceHandle_t>
204+
%template(InstanceHandleVector) std::vector<eprosima::fastrtps::rtps::InstanceHandle_t>;
205+
%typemap(doctype) std::vector<eprosima::fastrtps::rtps::InstanceHandle_t>"InstanceHandleVector";
206+
207+
%include "fastdds/rtps/common/InstanceHandle.h"
208+
209+
%pythoncode %{
210+
def _ihv_get_value(self):
211+
return self.to_sequence()
212+
213+
def _ihv_set_value(self, seq):
214+
self.from_sequence(seq)
215+
216+
InstanceHandle_t.value = property(_ihv_get_value, _ihv_set_value,
217+
doc="16-byte value as list/tuple/bytes/bytearray")
218+
%}

fastdds_python/test/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,17 @@ if (${fastcdr_VERSION_MAJOR} EQUAL 1)
2020
set(fastcdr_version_argument "v1")
2121
endif()
2222

23+
# Unit tests
24+
add_test(NAME unit_tests
25+
COMMAND
26+
${Python3_EXECUTABLE}
27+
-m pytest
28+
-vrP
29+
WORKING_DIRECTORY
30+
${CMAKE_CURRENT_SOURCE_DIR}/unittest
31+
)
2332

33+
# DDS Api tests
2434
add_test(NAME api_tests
2535
COMMAND
2636
${Python3_EXECUTABLE}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# until https://bugs.python.org/issue46276 is not fixed we can apply this
2+
# workaround on windows
3+
import os
4+
5+
if os.name == "nt":
6+
import win32api
7+
8+
win32api.LoadLibrary("fastrtps")
9+
10+
import fastdds
11+
import pytest
12+
import sys
13+
14+
15+
def test_create_instance_handle_from_bytes():
16+
ih = fastdds.InstanceHandle_t()
17+
ih.value = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
18+
assert (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) == ih.value
19+
20+
21+
def test_create_instance_handle_from_bytearray():
22+
ih = fastdds.InstanceHandle_t()
23+
ih.value = bytearray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
24+
assert (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) == ih.value
25+
26+
27+
def test_create_instance_handle_from_tuple():
28+
ih = fastdds.InstanceHandle_t()
29+
ih.value = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
30+
assert (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) == ih.value
31+
32+
33+
def test_create_instance_handle_from_list():
34+
ih = fastdds.InstanceHandle_t()
35+
ih.value = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
36+
assert (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) == ih.value
37+
38+
39+
def test_create_instance_handle_from_bytes_with_less_elements():
40+
ih = fastdds.InstanceHandle_t()
41+
with pytest.raises(SystemError) as exception:
42+
ih.value = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
43+
repr = exception.getrepr()
44+
assert str(repr).split("\n")[0] == "ValueError: Expected 16 bytes"
45+
46+
47+
def test_create_instance_handle_from_bytes_with_more_elements():
48+
ih = fastdds.InstanceHandle_t()
49+
with pytest.raises(SystemError) as exception:
50+
ih.value = (
51+
b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12"
52+
)
53+
repr = exception.getrepr()
54+
assert str(repr).split("\n")[0] == "ValueError: Expected 16 bytes"
55+
56+
57+
def test_create_instance_handle_from_bytearray_with_less_elements():
58+
ih = fastdds.InstanceHandle_t()
59+
with pytest.raises(SystemError) as exception:
60+
ih.value = bytearray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
61+
repr = exception.getrepr()
62+
assert str(repr).split("\n")[0] == "ValueError: Expected 16 bytes"
63+
64+
65+
def test_create_instance_handle_from_bytearray_with_more_elements():
66+
ih = fastdds.InstanceHandle_t()
67+
with pytest.raises(SystemError) as exception:
68+
ih.value = bytearray(
69+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
70+
)
71+
repr = exception.getrepr()
72+
assert str(repr).split("\n")[0] == "ValueError: Expected 16 bytes"
73+
74+
75+
def test_create_instance_handle_from_tuple_with_less_elements():
76+
ih = fastdds.InstanceHandle_t()
77+
with pytest.raises(SystemError) as exception:
78+
ih.value = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
79+
repr = exception.getrepr()
80+
assert str(repr).split("\n")[0] == "ValueError: Expected 16 elements"
81+
82+
83+
def test_create_instance_handle_from_tuple_with_more_elements():
84+
ih = fastdds.InstanceHandle_t()
85+
with pytest.raises(SystemError) as exception:
86+
ih.value = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)
87+
repr = exception.getrepr()
88+
assert str(repr).split("\n")[0] == "ValueError: Expected 16 elements"
89+
90+
91+
def test_create_instance_handle_from_list_with_less_elements():
92+
ih = fastdds.InstanceHandle_t()
93+
with pytest.raises(SystemError) as exception:
94+
ih.value = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
95+
repr = exception.getrepr()
96+
assert str(repr).split("\n")[0] == "ValueError: Expected 16 elements"
97+
98+
99+
def test_create_instance_handle_from_list_with_more_elements():
100+
ih = fastdds.InstanceHandle_t()
101+
with pytest.raises(SystemError) as exception:
102+
ih.value = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
103+
repr = exception.getrepr()
104+
assert str(repr).split("\n")[0] == "ValueError: Expected 16 elements"
105+
106+
107+
def test_create_instance_handle_from_bytearray_with_with_negative_number():
108+
ih = fastdds.InstanceHandle_t()
109+
with pytest.raises(ValueError) as exception:
110+
ih.value = bytearray([1, 2, 3, 4, 5, 6, 7, 8, 9, -10, 11, 12, 13, 14, 15, 16])
111+
assert str(exception.value) == "byte must be in range(0, 256)"
112+
113+
114+
def test_create_instance_handle_from_bytearray_with_large_number():
115+
ih = fastdds.InstanceHandle_t()
116+
with pytest.raises(ValueError) as exception:
117+
ih.value = bytearray([1, 2, 3, 4, 5, 6, 7, 8, 9, 1000, 11, 12, 13, 14, 15, 16])
118+
assert str(exception.value) == "byte must be in range(0, 256)"
119+
120+
121+
def test_create_instance_handle_from_tuple_with_negative_number():
122+
ih = fastdds.InstanceHandle_t()
123+
with pytest.raises(SystemError) as exception:
124+
ih.value = (1, 2, 3, 4, 5, 6, 7, 8, 9, -10, 11, 12, 13, 14, -15, 16)
125+
repr = exception.getrepr()
126+
assert str(repr).split("\n")[0] == "ValueError: Each value must be in 0..255"
127+
128+
129+
def test_create_instance_handle_from_tuple_with_large_number():
130+
ih = fastdds.InstanceHandle_t()
131+
with pytest.raises(SystemError) as exception:
132+
ih.value = (1, 2, 3, 4, 5, 6, 7, 8, 9, 1000, 11, 12, 13, 14, 15, 16)
133+
repr = exception.getrepr()
134+
assert str(repr).split("\n")[0] == "ValueError: Each value must be in 0..255"
135+
136+
137+
def test_create_instance_handle_from_list_with_negative_number():
138+
ih = fastdds.InstanceHandle_t()
139+
with pytest.raises(SystemError) as exception:
140+
ih.value = [1, 2, 3, 4, 5, 6, 7, 8, 9, -10, 11, 12, 13, 14, 15, 16]
141+
repr = exception.getrepr()
142+
assert str(repr).split("\n")[0] == "ValueError: Each value must be in 0..255"
143+
144+
145+
def test_create_instance_handle_from_list_with_large_number():
146+
ih = fastdds.InstanceHandle_t()
147+
with pytest.raises(SystemError) as exception:
148+
ih.value = [1, 2, 3, 4, 5, 6, 7, 8, 9, 1000, 11, 12, 13, 14, 15, 16]
149+
repr = exception.getrepr()
150+
assert str(repr).split("\n")[0] == "ValueError: Each value must be in 0..255"

0 commit comments

Comments
 (0)