Skip to content
Snippets Groups Projects
Commit 0f9a06d1 authored by Olivier BICHLER's avatar Olivier BICHLER
Browse files

Working version with Python

parent 563b22e0
No related branches found
No related tags found
2 merge requests!212Version 0.3.0,!161Add support for namespaced attributes in DynamicAttributes
Pipeline #50512 failed
...@@ -73,15 +73,26 @@ class test_operator_binding(unittest.TestCase): ...@@ -73,15 +73,26 @@ class test_operator_binding(unittest.TestCase):
self.assertEqual(attrs.get_attr("b"), "test") self.assertEqual(attrs.get_attr("b"), "test")
self.assertEqual(attrs.has_attr("c"), True) self.assertEqual(attrs.has_attr("c"), True)
self.assertEqual(attrs.get_attr("c"), [True, False, True]) self.assertEqual(attrs.get_attr("c"), [True, False, True])
self.assertEqual(attrs.dict().keys(), {"a", "b", "c"}) self.assertEqual(attrs.dict().keys(), {"a", "b", "c", "mem", "impl"})
self.assertEqual(attrs.has_attr("d"), False) self.assertEqual(attrs.has_attr("d"), False)
self.assertEqual(attrs.has_attr("mem.a"), True)
self.assertEqual(attrs.get_attr("mem.a"), 1)
self.assertEqual(attrs.has_attr("mem.data.b"), True)
self.assertEqual(attrs.get_attr("mem.data.b"), 1.0)
self.assertEqual(attrs.get_attr("mem").get_attr("data").get_attr("b"), 1.0)
self.assertEqual(attrs.has_attr("impl.c"), True)
self.assertEqual(attrs.get_attr("impl.c"), "test")
# Add Python attributes # Add Python attributes
attrs.add_attr("d", 18.56) attrs.add_attr("d", 18.56)
self.assertEqual(attrs.get_attr("d"), 18.56) self.assertEqual(attrs.get_attr("d"), 18.56)
self.assertEqual(attrs.has_attr("d"), True) self.assertEqual(attrs.has_attr("d"), True)
self.assertEqual(attrs.dict().keys(), {"a", "b", "c", "d"}) self.assertEqual(attrs.dict().keys(), {"a", "b", "c", "d", "mem", "impl"})
self.assertEqual(attrs.has_attr("e"), False) self.assertEqual(attrs.has_attr("e"), False)
attrs.add_attr("mem.data.c", 19.36)
self.assertEqual(attrs.get_attr("mem.data.c"), 19.36)
self.assertEqual(attrs.has_attr("mem.data.c"), True)
self.assertEqual(attrs.dict().keys(), {"a", "b", "c", "d", "mem", "impl"})
# Check that added Python attribute is accessible in C++ # Check that added Python attribute is accessible in C++
# Return the value of an attribute named "d" of type float64 (double in C++) # Return the value of an attribute named "d" of type float64 (double in C++)
......
...@@ -67,19 +67,8 @@ public: ...@@ -67,19 +67,8 @@ public:
} }
else { else {
const auto ns = name.substr(0, dot); const auto ns = name.substr(0, dot);
AIDGE_ASSERT(isPascalCase(ns), "Aidge standard requires PascalCase for C++ Attributes namespace for \"{}\".", ns);
const auto nsName = name.substr(dot + 1); const auto nsName = name.substr(dot + 1);
#ifdef PYBIND
// If attribute does not exist in C++, it might have been created or modified in Python
auto it = mAttrs.find(ns);
if (it == mAttrs.end()) {
auto itPy = mAttrsPy.find(ns);
if (itPy != mAttrsPy.end()) {
// Insert the attribute back in C++
mAttrs.emplace(std::make_pair(ns, future_std::any(itPy->second.cast<DynamicAttributes>())));
}
}
#endif
return future_std::any_cast<const DynamicAttributes&>(mAttrs.at(ns)).getAttr<T>(nsName); return future_std::any_cast<const DynamicAttributes&>(mAttrs.at(ns)).getAttr<T>(nsName);
} }
} }
...@@ -106,23 +95,16 @@ public: ...@@ -106,23 +95,16 @@ public:
// We cannot handle Python object if the Python interpreter is not running // We cannot handle Python object if the Python interpreter is not running
if (Py_IsInitialized()) { if (Py_IsInitialized()) {
// Keep a copy of the attribute in py::object that is updated everytime // Keep a copy of the attribute in py::object that is updated everytime
mAttrsPy.emplace(std::make_pair(pascalToSnake(name), py::cast(value))); const auto& resPy = mAttrsPy.emplace(std::make_pair(pascalToSnake(name), py::cast(value)));
AIDGE_ASSERT(resPy.second, "attribute \"{}\" already exists (added in Python)", name);
} }
#endif #endif
} }
else { else {
const auto ns = name.substr(0, dot); const auto ns = name.substr(0, dot);
AIDGE_ASSERT(isPascalCase(ns), "Aidge standard requires PascalCase for C++ Attributes namespace for \"{}\".", ns);
const auto nsName = name.substr(dot + 1); const auto nsName = name.substr(dot + 1);
const auto& res = mAttrs.emplace(std::make_pair(ns, future_std::any(DynamicAttributes()))); const auto& res = mAttrs.emplace(std::make_pair(ns, future_std::any(DynamicAttributes())));
#ifdef PYBIND
// We cannot handle Python object if the Python interpreter is not running
if (Py_IsInitialized()) {
// Keep a copy of the attribute in py::object that is updated everytime
mAttrsPy.emplace(std::make_pair(ns, py::cast(DynamicAttributes())));
}
#endif
future_std::any_cast<DynamicAttributes&>(res.first->second).addAttr(nsName, value); future_std::any_cast<DynamicAttributes&>(res.first->second).addAttr(nsName, value);
} }
} }
...@@ -135,6 +117,7 @@ public: ...@@ -135,6 +117,7 @@ public:
{ {
const auto dot = name.find('.'); const auto dot = name.find('.');
if (dot == name.npos) { if (dot == name.npos) {
AIDGE_ASSERT(isPascalCase(name), "Aidge standard requires PascalCase for C++ Attributes for \"{}\".", name);
auto res = mAttrs.emplace(std::make_pair(name, future_std::any(value))); auto res = mAttrs.emplace(std::make_pair(name, future_std::any(value)));
if (!res.second) if (!res.second)
res.first->second = future_std::any(value); res.first->second = future_std::any(value);
...@@ -151,17 +134,9 @@ public: ...@@ -151,17 +134,9 @@ public:
} }
else { else {
const auto ns = name.substr(0, dot); const auto ns = name.substr(0, dot);
AIDGE_ASSERT(isPascalCase(ns), "Aidge standard requires PascalCase for C++ Attributes namespace for \"{}\".", ns);
const auto nsName = name.substr(dot + 1); const auto nsName = name.substr(dot + 1);
auto res = mAttrs.emplace(std::make_pair(ns, future_std::any(DynamicAttributes()))); auto res = mAttrs.emplace(std::make_pair(ns, future_std::any(DynamicAttributes())));
#ifdef PYBIND
// We cannot handle Python object if the Python interpreter is not running
if (Py_IsInitialized()) {
// Keep a copy of the attribute in py::object that is updated everytime
auto resPy = mAttrsPy.emplace(std::make_pair(ns, py::cast(DynamicAttributes())));
}
#endif
future_std::any_cast<DynamicAttributes&>(res.first->second).setAttr<T>(nsName, value); future_std::any_cast<DynamicAttributes&>(res.first->second).setAttr<T>(nsName, value);
} }
} }
...@@ -169,18 +144,17 @@ public: ...@@ -169,18 +144,17 @@ public:
void delAttr(const std::string& name) { void delAttr(const std::string& name) {
const auto dot = name.find('.'); const auto dot = name.find('.');
if (dot == name.npos) { if (dot == name.npos) {
AIDGE_ASSERT(isPascalCase(name), "Aidge standard requires PascalCase for C++ Attributes for \"{}\".", name);
mAttrs.erase(name); mAttrs.erase(name);
#ifdef PYBIND #ifdef PYBIND
mAttrsPy.erase(name); mAttrsPy.erase(pascalToSnake(name));
#endif #endif
} }
else { else {
const auto ns = name.substr(0, dot); const auto ns = name.substr(0, dot);
AIDGE_ASSERT(isPascalCase(ns), "Aidge standard requires PascalCase for C++ Attributes namespace for \"{}\".", ns);
const auto nsName = name.substr(dot + 1); const auto nsName = name.substr(dot + 1);
future_std::any_cast<DynamicAttributes&>(mAttrs.at(ns)).delAttr(nsName); future_std::any_cast<DynamicAttributes&>(mAttrs.at(ns)).delAttr(nsName);
#ifdef PYBIND
mAttrsPy.erase(name);
#endif
} }
} }
...@@ -191,15 +165,16 @@ public: ...@@ -191,15 +165,16 @@ public:
if (dot == name.npos) { if (dot == name.npos) {
AIDGE_ASSERT(isSnakeCase(name), "Aidge standard requires snake_case for Attributes with Python for \"{}\".", name); AIDGE_ASSERT(isSnakeCase(name), "Aidge standard requires snake_case for Attributes with Python for \"{}\".", name);
auto it = mAttrs.find(snakeToPascal(name)); auto it = mAttrs.find(snakeToPascal(name));
AIDGE_ASSERT(it == mAttrs.end(), "attribute \"{}\" already exists", name); AIDGE_ASSERT(it == mAttrs.end(), "attribute \"{}\" already exists (added in C++)", name);
const auto& res = mAttrsPy.emplace(std::make_pair(name, value)); const auto& res = mAttrsPy.emplace(std::make_pair(name, value));
AIDGE_ASSERT(res.second, "attribute \"{}\" already exists", name); AIDGE_ASSERT(res.second, "attribute \"{}\" already exists", name);
} }
else { else {
const auto ns = name.substr(0, dot); const auto ns = name.substr(0, dot);
AIDGE_ASSERT(isSnakeCase(ns), "Aidge standard requires snake_case for Attributes namespace with Python for \"{}\".", ns);
const auto nsName = name.substr(dot + 1); const auto nsName = name.substr(dot + 1);
const auto& res = mAttrs.emplace(std::make_pair(ns, future_std::any(DynamicAttributes()))); const auto& res = mAttrs.emplace(std::make_pair(snakeToPascal(ns), DynamicAttributes()));
future_std::any_cast<DynamicAttributes&>(res.first->second).addAttrPy(nsName, std::move(value)); future_std::any_cast<DynamicAttributes&>(res.first->second).addAttrPy(nsName, std::move(value));
} }
...@@ -220,8 +195,9 @@ public: ...@@ -220,8 +195,9 @@ public:
} }
else { else {
const auto ns = name.substr(0, dot); const auto ns = name.substr(0, dot);
AIDGE_ASSERT(isSnakeCase(ns), "Aidge standard requires snake_case for Attributes namespace with Python for \"{}\".", ns);
const auto nsName = name.substr(dot + 1); const auto nsName = name.substr(dot + 1);
const auto& res = mAttrs.emplace(std::make_pair(ns, future_std::any(DynamicAttributes()))); const auto& res = mAttrs.emplace(std::make_pair(snakeToPascal(ns), DynamicAttributes()));
future_std::any_cast<DynamicAttributes&>(res.first->second).setAttrPy(nsName, std::move(value)); future_std::any_cast<DynamicAttributes&>(res.first->second).setAttrPy(nsName, std::move(value));
} }
...@@ -229,6 +205,12 @@ public: ...@@ -229,6 +205,12 @@ public:
py::dict dict() const override { py::dict dict() const override {
py::dict attributes; py::dict attributes;
for (const auto& elt : mAttrs) {
const std::string snakeName = pascalToSnake(elt.first);
if (elt.second.type() == typeid(DynamicAttributes)) {
attributes[snakeName.c_str()] = future_std::any_cast<const DynamicAttributes&>(elt.second).dict();
}
}
for (const auto& elt : mAttrsPy) { for (const auto& elt : mAttrsPy) {
const std::string snakeName = pascalToSnake(elt.first); const std::string snakeName = pascalToSnake(elt.first);
attributes[snakeName.c_str()] = elt.second; attributes[snakeName.c_str()] = elt.second;
...@@ -255,10 +237,16 @@ public: ...@@ -255,10 +237,16 @@ public:
const auto dot = name.find('.'); const auto dot = name.find('.');
if (dot == name.npos) { if (dot == name.npos) {
AIDGE_ASSERT(isPascalCase(name), "Aidge standard requires PascalCase for C++ Attributes for \"{}\".", name); AIDGE_ASSERT(isPascalCase(name), "Aidge standard requires PascalCase for C++ Attributes for \"{}\".", name);
#ifdef PYBIND
return (mAttrs.find(name) != mAttrs.cend() || mAttrsPy.find(pascalToSnake(name)) != mAttrsPy.cend());
#else
return (mAttrs.find(name) != mAttrs.cend()); return (mAttrs.find(name) != mAttrs.cend());
#endif
} }
else { else {
const auto ns = name.substr(0, dot); const auto ns = name.substr(0, dot);
AIDGE_ASSERT(isPascalCase(ns), "Aidge standard requires PascalCase for C++ Attributes namespace \"{}\".", ns);
const auto it = mAttrs.find(ns); const auto it = mAttrs.find(ns);
if (it != mAttrs.cend()) { if (it != mAttrs.cend()) {
const auto nsName = name.substr(dot + 1); const auto nsName = name.substr(dot + 1);
...@@ -280,19 +268,14 @@ public: ...@@ -280,19 +268,14 @@ public:
} }
else { else {
const auto ns = name.substr(0, dot); const auto ns = name.substr(0, dot);
const auto nsName = name.substr(dot + 1); AIDGE_ASSERT(isSnakeCase(ns), "Aidge standard requires snake_case for Attributes namespace with Python for \"{}\".", ns);
const auto it = mAttrs.find(ns); const auto it = mAttrs.find(snakeToPascal(ns));
if (it != mAttrs.cend()) { if (it != mAttrs.cend()) {
const auto nsName = name.substr(dot + 1);
return future_std::any_cast<const DynamicAttributes&>(it->second).hasAttrPy(nsName); return future_std::any_cast<const DynamicAttributes&>(it->second).hasAttrPy(nsName);
} }
else { else {
const auto itPy = mAttrsPy.find(ns); return false;
if (itPy != mAttrsPy.cend()) {
return itPy->second.cast<DynamicAttributes>().hasAttrPy(nsName);
}
else {
return false;
}
} }
} }
} }
...@@ -320,18 +303,6 @@ public: ...@@ -320,18 +303,6 @@ public:
else { else {
const auto ns = name.substr(0, dot); const auto ns = name.substr(0, dot);
const auto nsName = name.substr(dot + 1); const auto nsName = name.substr(dot + 1);
#ifdef PYBIND
// If attribute does not exist in C++, it might have been created in Python
auto it = mAttrs.find(ns);
if (it == mAttrs.end()) {
auto itPy = mAttrsPy.find(ns);
if (itPy != mAttrsPy.end()) {
return itPy->second.cast<DynamicAttributes>().getAttrType(nsName);
}
}
#endif
return future_std::any_cast<const DynamicAttributes&>(mAttrs.at(ns)).getAttrType(nsName); return future_std::any_cast<const DynamicAttributes&>(mAttrs.at(ns)).getAttrType(nsName);
} }
} }
...@@ -357,12 +328,24 @@ public: ...@@ -357,12 +328,24 @@ public:
inline py::object getAttrPy(const std::string& name) const override final { inline py::object getAttrPy(const std::string& name) const override final {
const auto dot = name.find('.'); const auto dot = name.find('.');
if (dot == name.npos) { if (dot == name.npos) {
return mAttrsPy.at(name); AIDGE_ASSERT(isSnakeCase(name), "Aidge standard requires snake_case for Attributes with Python for \"{}\".", name);
auto itPy = mAttrsPy.find(name);
if (itPy == mAttrsPy.end()) {
// Attribute may be a namespace
auto it = mAttrs.find(snakeToPascal(name));
AIDGE_ASSERT(it != mAttrs.end() && it->second.type() == typeid(DynamicAttributes), "attribute \"{}\" not found", name);
return py::cast(future_std::any_cast<const DynamicAttributes&>(it->second));
}
else {
return itPy->second;
}
} }
else { else {
// Namespace is stored in mAttrs, so convention should be pascal.
const auto ns = name.substr(0, dot); const auto ns = name.substr(0, dot);
AIDGE_ASSERT(isSnakeCase(ns), "Aidge standard requires snake_case for Attributes namespace with Python for \"{}\".", ns);
const auto nsName = name.substr(dot + 1); const auto nsName = name.substr(dot + 1);
return mAttrsPy.at(ns).cast<DynamicAttributes>().getAttrPy(nsName); return future_std::any_cast<const DynamicAttributes&>(mAttrs.at(snakeToPascal(ns))).getAttrPy(nsName);
} }
}; };
#endif #endif
......
...@@ -24,6 +24,9 @@ DynamicAttributes test_DynamicAttributes_binding() { ...@@ -24,6 +24,9 @@ DynamicAttributes test_DynamicAttributes_binding() {
attrs.addAttr<int>("A", 42); attrs.addAttr<int>("A", 42);
attrs.addAttr<std::string>("B", "test"); attrs.addAttr<std::string>("B", "test");
attrs.addAttr<std::vector<bool>>("C", {true, false, true}); attrs.addAttr<std::vector<bool>>("C", {true, false, true});
attrs.addAttr("Mem.A", 1);
attrs.addAttr("Mem.Data.B", 1.0f);
attrs.addAttr("Impl.C", std::string("test"));
return attrs; return attrs;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment