diff --git a/host/lib/types/types_python.hpp b/host/lib/types/types_python.hpp index f1c1e874f..ba5707461 100644 --- a/host/lib/types/types_python.hpp +++ b/host/lib/types/types_python.hpp @@ -9,6 +9,7 @@ #define INCLUDED_UHD_TYPES_PYTHON_HPP #include +#include #include #include #include @@ -44,9 +45,82 @@ void export_types(py::module& m) .def("__str__", &uhd::device_addr_t::to_pp_string) .def("to_string", &uhd::device_addr_t::to_string) .def("to_pp_string", &uhd::device_addr_t::to_pp_string) - .def("to_dict", [](uhd::device_addr_t& self) { - return static_cast>(self); - }); + .def("to_dict", + [](uhd::device_addr_t& self) { + return static_cast>(self); + }) + .def("keys", [](const uhd::device_addr_t& self) { return self.keys(); }) + .def("values", [](const uhd::device_addr_t& self) { return self.vals(); }) + .def("get", + [](const uhd::device_addr_t& self, const std::string& key) -> py::object { + if (self.has_key(key)) { + return py::str(self.get(key)); + } + return py::none(); + }) + .def("get", + [](const uhd::device_addr_t& self, + const std::string& key, + const std::string& other) { return self.get(key, other); }) + .def( + "pop", + [](uhd::device_addr_t& self, + const std::string& key, + const char* def) -> std::string { + if (!self.has_key(key)) { + if (def == nullptr) { + throw py::key_error(key); + } + return std::string(def); + } + return self.pop(key); + }, + py::arg("k"), + py::arg("d") = nullptr) + .def( + "update", + [](uhd::device_addr_t& self, + const uhd::device_addr_t& new_dict, + const bool fail_on_conflict) { self.update(new_dict, fail_on_conflict); }, + py::arg("E"), + py::arg("fail_on_conflict") = true) + .def( + "update", + [](uhd::device_addr_t& self, + const std::map& new_dict, + const bool fail_on_conflict) { + self.update(uhd::device_addr_t(new_dict), fail_on_conflict); + }, + py::arg("E"), + py::arg("fail_on_conflict") = true) + .def("separate", + [](const uhd::device_addr_t& self) { + return uhd::separate_device_addr(self); + }) + .def("__getitem__", + [](const uhd::device_addr_t& self, const std::string& key) -> std::string { + if (!self.has_key(key)) { + throw py::key_error(key); + } + return self.get(key); + }) + .def("__setitem__", + [](uhd::device_addr_t& self, const std::string& key, const std::string& val) { + self.set(key, val); + }) + .def("__contains__", + [](const uhd::device_addr_t& self, const std::string& key) { + return self.has_key(key); + }) + .def("__len__", [](const uhd::device_addr_t& self) { return self.size(); }) + .def("__eq__", + [](const uhd::device_addr_t& self, const uhd::device_addr_t& other) { + return self == other; + }) + .def("__ne__", + [](const uhd::device_addr_t& self, const uhd::device_addr_t& other) { + return self != other; + }); // This will allow functions in Python that take a device_addr to also take // a string: py::implicitly_convertible(); diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index f6dfddfe3..b8a07d341 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -72,6 +72,7 @@ set(pytest_sources verify_fbs_test.py pychdr_parse_test.py uhd_image_downloader_test.py + device_addr_test.py ) #turn each test cpp file into an executable with an int main() function diff --git a/host/tests/device_addr_test.py b/host/tests/device_addr_test.py new file mode 100644 index 000000000..85cd5b73c --- /dev/null +++ b/host/tests/device_addr_test.py @@ -0,0 +1,56 @@ +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Unit test for uhd.types.DeviceAddr +""" + +import unittest +from uhd.types import DeviceAddr, separate_device_addr, combine_device_addrs + +class DeviceAddrTest(unittest.TestCase): + """ Test Python-wrapped device_addr_t """ + + def test_device_addr_dict(self): + """ + Check we can use DeviceAddr like a Python dict + """ + dev_a = DeviceAddr("a=1,b=2,c=3") + self.assertEqual(len(dev_a), 3) + self.assertEqual(dev_a.to_dict(), {'a': '1', 'b': '2', 'c': '3'}) + dev_d = dev_a.to_dict() + self.assertEqual(dev_a.keys(), list(dev_d.keys())) + self.assertEqual(dev_a.values(), list(dev_d.values())) + self.assertEqual(dev_a.get('a'), '1') + self.assertEqual(dev_a.get('a', 'boo'), '1') + self.assertEqual(dev_a.get('z'), None) + self.assertEqual(dev_a.get('z', 'boo'), 'boo') + self.assertEqual(dev_a['a'], '1') + with self.assertRaises(KeyError): + print(dev_a['z']) + self.assertTrue('a' in dev_a) + self.assertEqual(dev_a.pop('a'), '1') + self.assertEqual(len(dev_a), 2) + self.assertTrue('a' not in dev_a) + self.assertEqual(dev_a.pop('z', 'boo'), 'boo') + with self.assertRaises(KeyError): + print(dev_a.pop('z')) + dev_a.update({'a': '9'}) + self.assertEqual(dev_a.get('a'), '9') + with self.assertRaises(RuntimeError): + dev_a.update({'a': '12'}) + dev_a.update({'a': '12'}, False) + self.assertEqual(dev_a.get('a'), '12') + + def test_device_addr_separate(self): + dev_a = DeviceAddr("addr0=192.168.40.1,addr1=192.168.40.2,time_source=external") + self.assertEqual(len(dev_a.separate()), 2) + self.assertEqual(len(dev_a.separate()[0]), 2) + self.assertEqual(dev_a.separate(), separate_device_addr(dev_a)) + self.assertEqual( + DeviceAddr( + 'addr0=192.168.40.1,time_source0=external,' + 'addr1=192.168.40.2,time_source1=external'), + combine_device_addrs(dev_a.separate()))