diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index b1c6b2c9d..4bd79b143 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -39,6 +39,7 @@ endfunction() pybind11_add_module(sdformat SHARED src/sdf/_ignition_sdformat_pybind11.cc src/sdf/pyError.cc + src/sdf/pyParserConfig.cc ) target_link_libraries(sdformat PRIVATE @@ -48,8 +49,18 @@ target_link_libraries(sdformat PRIVATE configure_build_install_location(sdformat) if (BUILD_TESTING) + pybind11_add_module(sdformattest SHARED + test/_ignition_sdformattest_pybind11.cc + ) + + target_link_libraries(sdformattest PRIVATE + ${PROJECT_LIBRARY_TARGET_NAME} + ignition-utils${IGN_UTILS_VER}::ignition-utils${IGN_UTILS_VER} + ) + set(python_tests pyError_TEST + pyParserConfig_TEST ) foreach (test ${python_tests}) diff --git a/python/src/sdf/_ignition_sdformat_pybind11.cc b/python/src/sdf/_ignition_sdformat_pybind11.cc index b466f0a19..83ac08f47 100644 --- a/python/src/sdf/_ignition_sdformat_pybind11.cc +++ b/python/src/sdf/_ignition_sdformat_pybind11.cc @@ -17,9 +17,11 @@ #include #include "pyError.hh" +#include "pyParserConfig.hh" PYBIND11_MODULE(sdformat, m) { m.doc() = "sdformat Python Library."; sdf::python::defineError(m); + sdf::python::defineParserConfig(m); } diff --git a/python/src/sdf/pyParserConfig.cc b/python/src/sdf/pyParserConfig.cc new file mode 100644 index 000000000..6b180b526 --- /dev/null +++ b/python/src/sdf/pyParserConfig.cc @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pyParserConfig.hh" + +#include +#include +#include +#include + +#include "sdf/ParserConfig.hh" + +namespace sdf +{ +// Inline bracket to help doxygen filtering. +inline namespace SDF_VERSION_NAMESPACE { +namespace python +{ +///////////////////////////////////////////////// +void defineParserConfig(pybind11::object module) +{ + pybind11::class_ parseConfigModule(module, "ParserConfig"); + parseConfigModule + .def(pybind11::init<>()) + .def(pybind11::init()) + .def("global_config", + &sdf::ParserConfig::GlobalConfig, + "Mutable access to a singleton ParserConfig that serves as the global " + "ParserConfig object for all parsing operations that do not specify " + "their own ParserConfig") + .def("find_file_callback", + &sdf::ParserConfig::FindFileCallback, + "Get the find file callback function") + .def("set_find_callback", + &sdf::ParserConfig::SetFindCallback, + "Set the callback to use when libsdformat can't find a file.") + .def("uri_path_map", + &sdf::ParserConfig::URIPathMap, + "Get the URI scheme to search directories map") + .def("add_uri_path", + &sdf::ParserConfig::AddURIPath, + "Associate paths to a URI.") + .def("set_warnings_policy", + &sdf::ParserConfig::SetWarningsPolicy, + "Set the warning enforcment policy.") + .def("warnings_policy", + &sdf::ParserConfig::WarningsPolicy, + "Get the current warning enforcement policy") + .def("set_unrecognized_elements_policy", + &sdf::ParserConfig::SetUnrecognizedElementsPolicy, + "Set the policy for unrecogonized elements without an xmlns") + .def("unrecognized_elements_policy", + &sdf::ParserConfig::UnrecognizedElementsPolicy, + "Get the current unrecognized elements policy") + .def("set_deprecated_elements_policy", + &sdf::ParserConfig::SetDeprecatedElementsPolicy, + "Set the policy for deprecated elements.") + .def("reset_deprecated_elements_policy", + &sdf::ParserConfig::ResetDeprecatedElementsPolicy, + "Resets the policy for deprecated elements so that it follows " + "WarningsPolicy.") + .def("deprecated_elements_policy", + &sdf::ParserConfig::DeprecatedElementsPolicy, + "Get the current deprecated elements policy") + .def("urdf_set_preserve_fixed_joint", + &sdf::ParserConfig::URDFSetPreserveFixedJoint, + "Set the preserveFixedJoint flag.") + .def("urdf_preserve_fixed_joint", + &sdf::ParserConfig::URDFPreserveFixedJoint, + "Get the preserveFixedJoint flag value."); + + pybind11::enum_( + parseConfigModule, "EnforcementPolicy") + .value("ERR", sdf::EnforcementPolicy::ERR) + .value("WARN", sdf::EnforcementPolicy::WARN) + .value("LOG", sdf::EnforcementPolicy::LOG); + +} +} // namespace python +} // namespace SDF_VERSION_NAMESPACE +} // namespace sdf diff --git a/python/src/sdf/pyParserConfig.hh b/python/src/sdf/pyParserConfig.hh new file mode 100644 index 000000000..db240eac9 --- /dev/null +++ b/python/src/sdf/pyParserConfig.hh @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SDFORMAT_PYTHON_PARSERCONFIG_HH_ +#define SDFORMAT_PYTHON_PARSERCONFIG_HH_ + +#include + +#include "sdf/ParserConfig.hh" + +#include "sdf/config.hh" + +namespace sdf +{ +// Inline bracket to help doxygen filtering. +inline namespace SDF_VERSION_NAMESPACE { +namespace python +{ +/// Define a pybind11 wrapper for an sdf::ParserConfig +/** + * \param[in] module a pybind11 module to add the definition to + */ +void defineParserConfig(pybind11::object module); +} // namespace python +} // namespace SDF_VERSION_NAMESPACE +} // namespace sdf + +#endif // SDFORMAT_PYTHON_PARSERCONFIG_HH_ diff --git a/python/test/_ignition_sdformattest_pybind11.cc b/python/test/_ignition_sdformattest_pybind11.cc new file mode 100644 index 000000000..b833ca3c0 --- /dev/null +++ b/python/test/_ignition_sdformattest_pybind11.cc @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "test_config.h" + +PYBIND11_MODULE(sdformattest, m) { + m.doc() = "sdformat test Python Library."; + + m.def( + "source_file", + []() + { + return sdf::testing::SourceFile(); + }, + "Retrieve a file from the project source directory"); + + m.def( + "test_file", + []() + { + return sdf::testing::TestFile(); + }, + "Retrieve a file from the project source directory"); +} diff --git a/python/test/pyParserConfig_TEST.py b/python/test/pyParserConfig_TEST.py new file mode 100644 index 000000000..f36300090 --- /dev/null +++ b/python/test/pyParserConfig_TEST.py @@ -0,0 +1,90 @@ +# Copyright (C) 2022 Open Source Robotics Foundation + +# Licensed under the Apache License, Version 2.0 (the "License") +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from sdformat import ParserConfig +from sdformattest import source_file, test_file +import unittest + + +class ParserConfigColor(unittest.TestCase): + + def test_construction(self): + config = ParserConfig() + self.assertFalse(config.find_file_callback()) + self.assertFalse(config.uri_path_map()) + self.assertFalse(config.find_file_callback()) + + testDir = source_file(); + config.add_uri_path("file://", testDir) + + self.assertTrue(config.uri_path_map()) + it = config.uri_path_map().get("file://") + self.assertEqual(1, len(it)) + self.assertEqual(it[0], testDir) + + def testFunc(argument): + return "test/dir2"; + + config.set_find_callback(testFunc) + self.assertTrue(config.find_file_callback()) + self.assertEqual("test/dir2", config.find_file_callback()("empty")) + + + def test_copy_construction(self): + # The directory used in add_uri_path must exist in the filesystem + # so we'll use the source path + testDir1 = source_file() + testDir2 = test_file() + + config1 = ParserConfig() + config1.add_uri_path("file://", testDir1) + it = config1.uri_path_map().get("file://") + self.assertEqual(1, len(it)) + self.assertEqual(it[0], testDir1) + + config2 = ParserConfig(config1) + it = config2.uri_path_map().get("file://") + self.assertEqual(1, len(it)) + self.assertEqual(it[0], testDir1) + + config2.add_uri_path("file://", testDir2) + it = config2.uri_path_map().get("file://") + self.assertEqual(2, len(it)) + self.assertEqual(it[1], testDir2) + + # Updating config2 should not affect config1 + it = config1.uri_path_map().get("file://") + self.assertEqual(1, len(it)) + self.assertEqual(it[0], testDir1) + + def test_copy(self): + # The directory used in add_uri_path must exist in the filesystem, + # so we'll use the source path + testDir1 = source_file() + + config1 = ParserConfig(); + config1.add_uri_path("file://", testDir1) + + it = config1.uri_path_map().get("file://") + self.assertEqual(1, len(it)) + self.assertEqual(it[0], testDir1) + + config2 = config1 + it = config2.uri_path_map().get("file://") + self.assertEqual(1, len(it)) + self.assertEqual(it[0], testDir1) + + +if __name__ == '__main__': + unittest.main()