cyphesis/rules/python/CyPy_Element.cpp
Erik Ogenvik e1ab2f5f85 Handle native Python types instead of Message.
When handling properties and attributes in the Python code we now use
native types, together with ElementMap and ElementList.
2018-12-16 17:31:35 +01:00

581 lines
16 KiB
C++

#include <utility>
#include <utility>
/*
Copyright (C) 2018 Erik Ogenvik
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <common/compose.hpp>
#include <common/debug.h>
#include "CyPy_Element.h"
#include "CyPy_Operation.h"
#include "CyPy_Oplist.h"
#include "CyPy_Location.h"
#include "CyPy_RootEntity.h"
using Atlas::Message::Element;
using Atlas::Message::MapType;
using Atlas::Message::ListType;
/**
* Used when iterating over a List element.
*/
struct CyPy_ListElementIterator : Py::PythonClass<CyPy_ListElementIterator>
{
//The owning element. Reference count is incremented at construction and decremented at destruction.
CyPy_ElementList* m_element;
Atlas::Message::ListType::const_iterator iter;
CyPy_ListElementIterator(Py::PythonClassInstance* self, Py::Tuple& args, Py::Dict& kwds)
: PythonClass(self, args, kwds), m_element(nullptr)
{
throw Py::RuntimeError("Can not instantiate directly.");
}
CyPy_ListElementIterator(Py::PythonClassInstance* self, CyPy_ElementList* value)
: PythonClass(self),
m_element(value),
iter(m_element->m_value.begin())
{
m_element->self().increment_reference_count();
}
~CyPy_ListElementIterator() override
{
m_element->self().decrement_reference_count();
}
PyObject* iternext()
{
if (iter != m_element->m_value.end()) {
auto wrapper = CyPy_Element::wrap(*iter);
wrapper.increment_reference_count();
*(iter)++;
return wrapper.ptr();
} else {
return nullptr;
}
}
static void init_type()
{
behaviors().name("Element list iterator");
behaviors().doc("");
behaviors().supportIter(Py::PythonType::support_iter_iternext);
behaviors().readyType();
}
static Py::PythonClassObject<CyPy_ListElementIterator> wrap(CyPy_ElementList* value)
{
auto obj = extension_object_new(type_object(), nullptr, nullptr);
reinterpret_cast<Py::PythonClassInstance*>(obj)->m_pycxx_object = new CyPy_ListElementIterator(reinterpret_cast<Py::PythonClassInstance*>(obj), value);
return Py::PythonClassObject<CyPy_ListElementIterator>(obj);
}
};
CyPy_ElementList::CyPy_ElementList(Py::PythonClassInstance* self, Py::Tuple& args, Py::Dict& kwds) :
WrapperBase(self, args, kwds)
{
for (auto entry : args) {
m_value.push_back(CyPy_Element::asElement(entry));
}
}
CyPy_ElementList::CyPy_ElementList(Py::PythonClassInstance* self, Atlas::Message::ListType element)
: WrapperBase(self, std::move(element))
{
}
void CyPy_ElementList::init_type()
{
behaviors().name("Element List");
behaviors().doc("");
behaviors().supportRepr();
behaviors().supportRichCompare();
behaviors().supportIter(Py::PythonType::support_iter_iter);
behaviors().supportSequenceType(Py::PythonType::support_sequence_length
| Py::PythonType::support_sequence_contains
| Py::PythonType::support_sequence_item
| Py::PythonType::support_sequence_ass_item
| Py::PythonType::support_sequence_repeat
| Py::PythonType::support_sequence_slice
| Py::PythonType::support_sequence_inplace_concat
| Py::PythonType::support_sequence_inplace_repeat);
behaviors().readyType();
CyPy_ListElementIterator::init_type();
}
Py::Object CyPy_ElementList::repr()
{
return Py::String(String::compose("<%1 object at %2>(%3)", type_object()->tp_name, this, debug_tostring(m_value)));
}
Py::Object CyPy_ElementList::rich_compare(const Py::Object& other, int op)
{
if ((op != Py_EQ) && (op != Py_NE)) {
throw Py::NotImplementedError("Element List object can only be check for == or !=.");
}
bool equal = false;
if (CyPy_ElementList::check(other)) {
equal = m_value == CyPy_ElementList::value(other);
}
if ((equal && op == Py_EQ) || (!equal && op == Py_NE)) {
return Py::True();
}
return Py::False();
}
Py::Object CyPy_ElementList::iter()
{
return CyPy_ListElementIterator::wrap(this);
}
PyCxx_ssize_t CyPy_ElementList::sequence_length()
{
return m_value.size();
}
Py::Object CyPy_ElementList::sequence_repeat(Py_ssize_t count)
{
Py::List list;
for (Py_ssize_t i = 0; i < count; ++count) {
for (auto& entry : m_value) {
list.append(CyPy_Element::asPyObject(entry, false));
}
}
return list;
}
int CyPy_ElementList::sequence_contains(const Py::Object& object)
{
auto element = CyPy_Element::asElement(object);
for (auto& entity : m_value) {
if (entity == element) {
return 1;
}
}
return 0;
}
Py::Object CyPy_ElementList::sequence_inplace_repeat(Py_ssize_t)
{
//TODO: implement
return self();
}
Py::Object CyPy_ElementList::sequence_inplace_concat(const Py::Object&)
{
//TODO: implement
return self();
}
int CyPy_ElementList::sequence_ass_item(Py_ssize_t index, const Py::Object& object)
{
if (index < static_cast<Py_ssize_t>(m_value.size())) {
m_value[index] = CyPy_Element::asElement(object);
return 1;
}
return -1;
}
Py::Object CyPy_ElementList::sequence_item(Py_ssize_t index)
{
if (index < static_cast<Py_ssize_t>(m_value.size())) {
return CyPy_Element::asPyObject(m_value.at(static_cast<unsigned long>(index)), false);
}
return Py::None();
}
Py::Object CyPy_ElementList::sequence_concat(const Py::Object& otherValue)
{
auto element = CyPy_Element::asElement(otherValue);
auto list = m_value;
list.push_back(element);
return wrap(list);
}
CyPy_ElementMap::CyPy_ElementMap(Py::PythonClassInstance* self, Py::Tuple& args, Py::Dict& kwds)
: WrapperBase(self, args, kwds)
{
for (auto entry : kwds) {
m_value.emplace(entry.first.as_string(), CyPy_Element::asElement(entry.second));
}
}
CyPy_ElementMap::CyPy_ElementMap(Py::PythonClassInstance* self, Atlas::Message::MapType element)
: WrapperBase(self, std::move(element))
{
}
void CyPy_ElementMap::init_type()
{
behaviors().name("Element Map");
behaviors().doc("");
behaviors().supportRepr();
behaviors().supportRichCompare();
behaviors().supportMappingType(Py::PythonType::support_mapping_ass_subscript
| Py::PythonType::support_mapping_subscript);
behaviors().readyType();
}
Py::Object CyPy_ElementMap::repr()
{
return Py::String(String::compose("<%1 object at %2>(%3)", type_object()->tp_name, this, debug_tostring(m_value)));
}
Py::Object CyPy_ElementMap::rich_compare(const Py::Object& other, int op)
{
if ((op != Py_EQ) && (op != Py_NE)) {
throw Py::NotImplementedError("Element Map object can only be check for == or !=.");
}
bool equal = false;
if (CyPy_ElementMap::check(other)) {
equal = m_value == CyPy_ElementMap::value(other);
}
if ((equal && op == Py_EQ) || (!equal && op == Py_NE)) {
return Py::True();
}
return Py::False();
}
Py::Object CyPy_ElementMap::getattro(const Py::String& name)
{
auto I = m_value.find(name);
if (I != m_value.end()) {
return CyPy_Element::asPyObject(I->second, false);
}
return PythonExtensionBase::getattro(name);
}
int CyPy_ElementMap::setattro(const Py::String& name, const Py::Object& attr)
{
m_value.emplace(name.as_string(), CyPy_Element::asElement(attr));
return 0;
}
Py::Object CyPy_ElementMap::mapping_subscript(const Py::Object& key)
{
auto I = m_value.find(verifyString(key));
if (I != m_value.end()) {
return CyPy_Element::asPyObject(I->second, false);
}
return Py::None();
}
int CyPy_ElementMap::mapping_ass_subscript(const Py::Object& key, const Py::Object& value)
{
m_value[verifyString(key)] = CyPy_Element::asElement(value);
return 0;
}
//
//CyPy_Element::CyPy_Element(Py::PythonClassInstance* self, Py::Tuple& args, Py::Dict& kwds) :
// WrapperBase(self, args, kwds)
//{
// if (args.size() > 1) {
// throw Py::TypeError("Must take max one argument.");
// }
// if (args.size()) {
// m_value = asElement(args.front());
// }
//}
//
//CyPy_Element::CyPy_Element(Py::PythonClassInstance* self, Atlas::Message::Element element)
// : WrapperBase(self, std::move(element))
//{
//}
//
//void CyPy_Element::init_type()
//{
// behaviors().name("Element");
// behaviors().doc("");
//
// behaviors().supportRepr();
// behaviors().supportRichCompare();
//
// behaviors().supportMappingType(Py::PythonType::support_mapping_ass_subscript
// | Py::PythonType::support_mapping_subscript);
// behaviors().supportIter(Py::PythonType::support_iter_iter);
// behaviors().supportSequenceType(Py::PythonType::support_sequence_length
// | Py::PythonType::support_sequence_contains
// | Py::PythonType::support_sequence_item
// | Py::PythonType::support_sequence_ass_item
// | Py::PythonType::support_sequence_repeat
// | Py::PythonType::support_sequence_slice
// | Py::PythonType::support_sequence_inplace_concat
// | Py::PythonType::support_sequence_inplace_repeat);
//
// PYCXX_ADD_NOARGS_METHOD(get_name, get_name, "");
// PYCXX_ADD_NOARGS_METHOD(pythonize, pythonize, "");
//
// behaviors().readyType();
//
// CyPy_ListElementIterator::init_type();
//}
//
//Py::Object CyPy_Element::get_name()
//{
// return Py::String("obj");
//}
//
//Py::Object CyPy_Element::pythonize()
//{
// return asPyObject(m_value, true);
//}
Py::Object CyPy_Element::mapAsPyObject(const MapType& map, bool useNativePythonType)
{
Py::Dict dict;
for (auto& entry : map) {
if (useNativePythonType) {
dict.setItem(entry.first, CyPy_Element::asPyObject(entry.second, useNativePythonType));
} else {
dict.setItem(entry.first, CyPy_Element::wrap(entry.second));
}
}
return dict;
}
Py::Object CyPy_Element::listAsPyObject(const ListType& list, bool useNativePythonType)
{
Py::List pyList;
for (auto& entry : list) {
if (useNativePythonType) {
pyList.append(CyPy_Element::asPyObject(entry, useNativePythonType));
} else {
pyList.append(CyPy_Element::wrap(entry));
}
}
return pyList;
}
Py::Object CyPy_Element::asPyObject(const Atlas::Message::Element& obj, bool useNativePythonType)
{
switch (obj.getType()) {
case Element::TYPE_INT:
return Py::Long(obj.Int());
case Element::TYPE_FLOAT:
return Py::Float(obj.Float());
case Element::TYPE_STRING:
return Py::String(obj.String());
case Element::TYPE_MAP:
return mapAsPyObject(obj.Map(), useNativePythonType);
case Element::TYPE_LIST:
return listAsPyObject(obj.List(), useNativePythonType);
default:
break;
}
return Py::None();
}
//Py::Object CyPy_Element::repr()
//{
// if (m_value.isString()) {
// return Py::String(m_value.String());
// }
// return Py::String(String::compose("<%1 object at %2>(%3)", type_object()->tp_name, this, debug_tostring(m_value)));
//}
//
//Py::Object CyPy_Element::rich_compare(const Py::Object& other, int op)
//{
// if (m_value.isFloat()) {
// if (op == Py_GT && other.isNumeric()) {
// return Py::Boolean(m_value.Float() > Py::Float(other));
// } else if (op == Py_GE && other.isNumeric()) {
// return Py::Boolean(m_value.Float() >= Py::Float(other));
// } else if (op == Py_LT && other.isNumeric()) {
// return Py::Boolean(m_value.Float() < Py::Float(other));
// } else if (op == Py_LE && other.isNumeric()) {
// return Py::Boolean(m_value.Float() <= Py::Float(other));
// }
// } else if (m_value.isInt()) {
// if (op == Py_GT && other.isNumeric()) {
// return Py::Boolean(m_value.Int() > Py::Long(other));
// } else if (op == Py_GE && other.isNumeric()) {
// return Py::Boolean(m_value.Int() >= Py::Long(other));
// } else if (op == Py_LT && other.isNumeric()) {
// return Py::Boolean(m_value.Int() < Py::Long(other));
// } else if (op == Py_LE && other.isNumeric()) {
// return Py::Boolean(m_value.Int() <= Py::Long(other));
// }
// }
//
//
// bool equal = false;
// if ((op != Py_EQ) && (op != Py_NE)) {
// throw Py::NotImplementedError("MessageElement object can only be check for == or !=, or <, <=, > and >= for ints and floats.");
// }
// if (m_value.isString()) {
// if (other.isString() &&
// m_value.asString() == Py::String(other).as_string()) {
// equal = true;
// }
// } else if (m_value.isInt()) {
// if (other.isLong() &&
// m_value.asInt() == Py::Long(other).as_long()) {
// equal = true;
// }
// } else if (m_value.isFloat()) {
// if (other.isFloat()
// && m_value.asFloat() == Py::Float(other).as_double()) {
// equal = true;
// }
// }
//
// if ((equal && op == Py_EQ) || (!equal && op == Py_NE)) {
// return Py::True();
// }
// return Py::False();
//}
ListType CyPy_Element::listAsElement(const Py::List& list)
{
ListType res;
for (auto& entry : list) {
res.push_back(asElement(entry));
}
return res;
}
MapType CyPy_Element::dictAsElement(const Py::Dict& dict)
{
MapType map;
for (auto key : dict.keys()) {
map.emplace(key.str(), asElement(dict.getItem(key)));
}
return map;
}
Element CyPy_Element::asElement(const Py::Object& o)
{
// if (CyPy_ElementList::check(o)) {
// return CyPy_ElementList::value(o);
// }
// if (CyPy_ElementMap::check(o)) {
// return CyPy_ElementMap::value(o);
// }
if (o.isLong()) {
return Py::Long(o).as_long();
}
if (o.isFloat()) {
return Py::Float(o).as_double();
}
if (o.isString()) {
return o.as_string();
}
if (CyPy_Operation::check(o)) {
return CyPy_Operation::value(o)->asMessage();
}
if (CyPy_RootEntity::check(o)) {
return CyPy_RootEntity::value(o)->asMessage();
}
if (CyPy_Oplist::check(o)) {
auto& oplist = CyPy_Oplist::value(o);
ListType list;
for (auto& entry : oplist) {
list.push_back(entry->asMessage());
}
return list;
}
if (CyPy_Location::check(o)) {
MapType map;
CyPy_Location::value(o).addToMessage(map);
return map;
}
if (o.isList()) {
return listAsElement(Py::List(o));
}
if (o.isDict()) {
return dictAsElement(Py::Dict(o));
}
if (o.isSequence()) {
Py::Sequence seq(o);
ListType list;
for (const auto& entry : seq) {
list.push_back(asElement(entry));
}
return list;
}
if (o.isNone()) {
return Element();
}
throw Py::TypeError(String::compose("Contained object (of type %1) could not be converted to an Element.", o.type().as_string()));
}
Py::Object CyPy_Element::wrap(Atlas::Message::Element value)
{
if (value.isNone()) {
return Py::None();
} else if (value.isString()) {
return Py::String(value.String());
} else if (value.isInt()) {
return Py::Long(value.Int());
} else if (value.isFloat()) {
return Py::Float(value.Float());
} else if (value.isList()) {
return CyPy_ElementList::wrap(value.List());
} else {
return CyPy_ElementMap::wrap(value.Map());
}
}
//
//void CyPy_Element::checkIsList()
//{
// if (!m_value.isList()) {
// throw Py::RuntimeError("Element is not of List type.");
// }
//
//}