From 717d988ac47c1ed4096de54a577b34453a4f33a9 Mon Sep 17 00:00:00 2001 From: Bram de Greve Date: Sat, 29 Dec 2018 14:38:51 +0100 Subject: [PATCH 1/2] Added a function to retrieve object from a module. --- lass/python/utilities.cpp | 25 +++++++++++++++++++++---- lass/python/utilities.h | 3 ++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/lass/python/utilities.cpp b/lass/python/utilities.cpp index a1dee572..a6b074d1 100644 --- a/lass/python/utilities.cpp +++ b/lass/python/utilities.cpp @@ -65,19 +65,36 @@ TPyObjPtr runString(const char *code, int start) /** @ingroup Python * @brief retrieve pointer to PyObject by its name in the script. * @return - * new reference to PyObject @a iName or NULL if - * @a iName does not exist (_without_ setting an exception!) + * new reference to PyObject @a name or NULL if + * @a name does not exist (_without_ setting an exception!) */ -TPyObjPtr getPyObjectByName(const std::string& iName) +TPyObjPtr getPyObjectByName(const std::string& name) { LockGIL LASS_UNUSED(lock); const TPyObjPtr dict = globals(); - const TPyObjPtr key(PY_ENFORCE_POINTER(pyBuildSimpleObject(iName))); + const TPyObjPtr key(PY_ENFORCE_POINTER(pyBuildSimpleObject(name))); PyObject* const object = PyDict_GetItem(dict.get(), key.get()); return fromNakedToSharedPtrCast(object); } +/** @ingroup Python + * @brief retrieve pointer to PyObject by its name in a module + * The module is imported by absolute import. + * @return + * new reference to PyObject @a name or NULL if + * @a name does not exist (_without_ setting an exception!) + */ +TPyObjPtr getPyObjectByName(const std::string& module, const std::string& name) +{ + LockGIL LASS_UNUSED(lock); + const TPyObjPtr mod(PyImport_ImportModule(module.c_str())); + PyObject* const dict = PY_ENFORCE_POINTER(PyModule_GetDict(mod.get())); // borrowed + const TPyObjPtr key(PY_ENFORCE_POINTER(pyBuildSimpleObject(name))); + PyObject* const object = PyDict_GetItem(dict, key.get()); + return fromNakedToSharedPtrCast(object); +} + /** @ingroup Python */ diff --git a/lass/python/utilities.h b/lass/python/utilities.h index 2eefa797..392ba75a 100644 --- a/lass/python/utilities.h +++ b/lass/python/utilities.h @@ -51,7 +51,8 @@ namespace lass namespace python { -LASS_PYTHON_DLL TPyObjPtr LASS_CALL getPyObjectByName(const std::string& iName); +LASS_PYTHON_DLL TPyObjPtr LASS_CALL getPyObjectByName(const std::string& name); +LASS_PYTHON_DLL TPyObjPtr LASS_CALL getPyObjectByName(const std::string& module, const std::string& name); LASS_PYTHON_DLL TPyObjPtr LASS_CALL globals(); LASS_PYTHON_DLL void LASS_CALL execute(const std::string& code); From 26a7cc7f16610ff98336f8a8d6d9ce8afd159551 Mon Sep 17 00:00:00 2001 From: Bram de Greve Date: Sat, 29 Dec 2018 14:35:57 +0100 Subject: [PATCH 2/2] Adding experimental PyDictPtr PyDictPtr is a wrapper around a TPyObjPtr that should always point to a PyDictObject (or be NULL). --- lass/python/pydict_ptr.cpp | 212 ++++++++++++++++++++++++++++++++ lass/python/pydict_ptr.h | 127 +++++++++++++++++++ test_suite/python_embedding.cpp | 37 ++++++ test_suite/test_bar.py | 13 ++ 4 files changed, 389 insertions(+) create mode 100644 lass/python/pydict_ptr.cpp create mode 100644 lass/python/pydict_ptr.h diff --git a/lass/python/pydict_ptr.cpp b/lass/python/pydict_ptr.cpp new file mode 100644 index 00000000..50fd8a11 --- /dev/null +++ b/lass/python/pydict_ptr.cpp @@ -0,0 +1,212 @@ +/** @file + * @author Bram de Greve (bram@cocamware.com) + * @author Tom De Muer (tom@cocamware.com) + * + * *** BEGIN LICENSE INFORMATION *** + * + * The contents of this file are subject to the Common Public Attribution License + * Version 1.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://lass.sourceforge.net/cpal-license. The License is based on the + * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover + * use of software over a computer network and provide for limited attribution for + * the Original Developer. In addition, Exhibit A has been modified to be consistent + * with Exhibit B. + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is LASS - Library of Assembled Shared Sources. + * + * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer. + * The Original Developer is the Initial Developer. + * + * All portions of the code written by the Initial Developer are: + * Copyright (C) 2004-2011 the Initial Developer. + * All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of the + * GNU General Public License Version 2 or later (the GPL), in which case the + * provisions of GPL are applicable instead of those above. If you wish to allow use + * of your version of this file only under the terms of the GPL and not to allow + * others to use your version of this file under the CPAL, indicate your decision by + * deleting the provisions above and replace them with the notice and other + * provisions required by the GPL License. If you do not delete the provisions above, + * a recipient may use your version of this file under either the CPAL or the GPL. + * + * *** END LICENSE INFORMATION *** + */ + +#include "python_common.h" +#include "pydict_ptr.h" + +namespace lass +{ +namespace python +{ +namespace experimental +{ + +PyDictPtr::PyDictPtr() : + obj_(0) +{ +} + + +/** + * steals a reference to obj. + */ +PyDictPtr::PyDictPtr(PyObject* obj): + obj_(obj) +{ + if (obj && !PyDict_Check(obj)) + throw PythonException(PyExc_TypeError, "Not a dict", LASS_HERE); +} + + +/** + * returns a borrowed reference + */ +PyObject* PyDictPtr::get() const +{ + return obj_.get(); +} + + +bool PyDictPtr::operator!() const +{ + return !obj_; +} + + +PyDictPtr::operator num::SafeBool() const +{ + return obj_ ? num::safeTrue : num::safeFalse; +} + + +void PyDictPtr::setItem(PyObject* key, PyObject* val) +{ + LockGIL LASS_UNUSED(lock); + PY_ENFORCE_ZERO(PyDict_SetItem(get(), key, val)); +} + + +void PyDictPtr::setItem(const TPyObjPtr& key, const TPyObjPtr& val) +{ + setItem(key.get(), val.get()); +} + + +TPyObjPtr PyDictPtr::getItem(PyObject* key) const +{ + LockGIL LASS_UNUSED(lock); +#if PY_MAJOR_VERSION >= 3 + PyObject* const borrowed = PyDict_GetItemWithError(get(), key); +#else + // for historical reasons, PyDict_GetItem PyDict_GetItem() suppresses all + // errors that may occur (like hash errors, or stack depth errors) + PyObject* const borrowed = PyDict_GetItem(get(), key); +#endif + if (!borrowed) + { + if (PyErr_Occurred()) + { + impl::fetchAndThrowPythonException(LASS_HERE); + } + throw PythonException(PyExc_KeyError, "Key error", LASS_HERE); + } + return fromNakedToSharedPtrCast(borrowed); +} + + +TPyObjPtr PyDictPtr::getItem(const TPyObjPtr& key) const +{ + return getItem(key.get()); +} + + +void PyDictPtr::delItem(PyObject* key) +{ + LockGIL LASS_UNUSED(lock); + if (PyDict_DelItem(get(), key) != 0) + impl::fetchAndThrowPythonException(LASS_HERE); +} + + +void PyDictPtr::delItem(const TPyObjPtr& key) +{ + delItem(key.get()); +} + + +bool PyDictPtr::contains(PyObject* key) const +{ + LockGIL LASS_UNUSED(lock); + switch (PyDict_Contains(get(), key)) + { + case -1: + impl::fetchAndThrowPythonException(LASS_HERE); + break; + case 0: + return false; + case 1: + return true; + } + // this should be unreachable. + throw PythonException(PyExc_SystemError, "bad internal call.", LASS_HERE); +} + + +bool PyDictPtr::contains(const TPyObjPtr& key) const +{ + return contains(key.get()); +} + + +Py_ssize_t PyDictPtr::size() const +{ + LockGIL LASS_UNUSED(lock); + const Py_ssize_t ret = PyDict_Size(get()); + if (ret == -1) + impl::fetchAndThrowPythonException(LASS_HERE); + return ret; +} + + +} + + +PyObject* PyExportTraits::build(const experimental::PyDictPtr& v) +{ + if (PyObject* const obj = v.get()) + { + Py_INCREF(obj); + return obj; + } + Py_RETURN_NONE; +} + + +int PyExportTraits::get(PyObject* obj, experimental::PyDictPtr& v) +{ + if (obj == Py_None) + { + v = experimental::PyDictPtr(0); + } + if (!PyDict_Check(obj)) + { + PyErr_SetString(PyExc_TypeError, "not a dict"); + return 1; + } + Py_INCREF(obj); // obj is borrowed, but PyDictPtr steals a reference + v = experimental::PyDictPtr(obj); + return 0; +} + + +} +} diff --git a/lass/python/pydict_ptr.h b/lass/python/pydict_ptr.h new file mode 100644 index 00000000..04a241fd --- /dev/null +++ b/lass/python/pydict_ptr.h @@ -0,0 +1,127 @@ +/** @file + * @author Bram de Greve (bram@cocamware.com) + * @author Tom De Muer (tom@cocamware.com) + * + * *** BEGIN LICENSE INFORMATION *** + * + * The contents of this file are subject to the Common Public Attribution License + * Version 1.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://lass.sourceforge.net/cpal-license. The License is based on the + * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover + * use of software over a computer network and provide for limited attribution for + * the Original Developer. In addition, Exhibit A has been modified to be consistent + * with Exhibit B. + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is LASS - Library of Assembled Shared Sources. + * + * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer. + * The Original Developer is the Initial Developer. + * + * All portions of the code written by the Initial Developer are: + * Copyright (C) 2004-2018 the Initial Developer. + * All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of the + * GNU General Public License Version 2 or later (the GPL), in which case the + * provisions of GPL are applicable instead of those above. If you wish to allow use + * of your version of this file only under the terms of the GPL and not to allow + * others to use your version of this file under the CPAL, indicate your decision by + * deleting the provisions above and replace them with the notice and other + * provisions required by the GPL License. If you do not delete the provisions above, + * a recipient may use your version of this file under either the CPAL or the GPL. + * + * *** END LICENSE INFORMATION *** + */ + +#ifndef LASS_GUARDIAN_OF_INCLUSION_PYTHON_PYDICT_PTR_H +#define LASS_GUARDIAN_OF_INCLUSION_PYTHON_PYDICT_PTR_H + +#include "python_common.h" +#include "pyobject_ptr.h" +#include "pyobject_plus.h" + +namespace lass +{ +namespace python +{ +namespace experimental +{ + +class LASS_PYTHON_DLL PyDictPtr +{ +public: + PyDictPtr(); + explicit PyDictPtr(PyObject* obj); + + PyObject* get() const; + bool operator!() const; + operator num::SafeBool() const; + + TPyObjPtr getItem(PyObject* key) const; + TPyObjPtr getItem(const TPyObjPtr& key) const; + template + TPyObjPtr getItem(const K& key) const + { + TPyObjPtr k(pyBuildSimpleObject(key)); + return getItem(k.get()); + } + + void setItem(PyObject* key, PyObject* val); + void setItem(const TPyObjPtr& key, const TPyObjPtr& val); + template + void setItem(const K& key, const V& val) + { + TPyObjPtr k(pyBuildSimpleObject(key)); + TPyObjPtr v(pyBuildSimpleObject(val)); + setItem(k.get(), v.get()); + } + + void delItem(PyObject* key); + void delItem(const TPyObjPtr& key); + template + void delItem(const K& key) + { + TPyObjPtr k(pyBuildSimpleObject(key)); + return delItem(k.get()); + } + + bool contains(PyObject* key) const; + bool contains(const TPyObjPtr& key) const; + template + bool contains(const K& key) const + { + TPyObjPtr k(pyBuildSimpleObject(key)); + return getItem(k.get()); + } + + Py_ssize_t size() const; + +private: + TPyObjPtr obj_; +}; + + +} + + +template <> +struct PyExportTraits +{ + LASS_PYTHON_DLL static PyObject* build(const experimental::PyDictPtr& v); + LASS_PYTHON_DLL static int get(PyObject* obj, experimental::PyDictPtr& v); +}; + + +} +} + +#endif + +// EOF diff --git a/test_suite/python_embedding.cpp b/test_suite/python_embedding.cpp index f9984d91..0fba6e94 100644 --- a/test_suite/python_embedding.cpp +++ b/test_suite/python_embedding.cpp @@ -46,6 +46,7 @@ #include "python_embedding.h" #include "../lass/python/python_api.h" #include "../lass/python/pyshadow_object.h" +#include "../lass/python/pydict_ptr.h" #include "foo.h" #include "bar.h" #include "python_shadow.h" @@ -497,6 +498,39 @@ std::wstring testStdWstring(const std::wstring& v) } +bool testPyDictPtr(lass::python::experimental::PyDictPtr dict) +{ + bool ok = true; + + ok &= (dict.size() == 2); + + ok &= dict.contains("a"); + lass::python::TPyObjPtr aObj = dict.getItem("a"); + ok &= !aObj.isEmpty(); + int a = 0; + ok &= (lass::python::pyGetSimpleObject(aObj.get(), a) == 0); + ok &= (a == 10); + + ok &= dict.contains(lass::python::makeTuple(1, 2)); + + dict.delItem("a"); + dict.delItem(lass::python::makeTuple(1, 2)); + + dict.setItem("b", 30); + dict.setItem(lass::python::makeTuple(3, 4), lass::python::makeTuple("c", "d")); + + return ok; +} + + +lass::python::experimental::PyDictPtr makePyDictPtr() +{ + lass::python::LockGIL LASS_UNUSED(lock); + lass::python::experimental::PyDictPtr dict(PyDict_New()); + dict.setItem("a", "b"); + return dict; +} + using namespace lass::test; @@ -548,6 +582,9 @@ PY_MODULE_FUNCTION( embedding, testSomePointer ) PY_MODULE_FUNCTION( embedding, testStdString ) PY_MODULE_FUNCTION( embedding, testStdWstring ) +PY_MODULE_FUNCTION( embedding, testPyDictPtr ) +PY_MODULE_FUNCTION( embedding, makePyDictPtr ) + PY_MODULE_INTEGER_CONSTANTS( embedding, emIsThis, emAnEnum ) diff --git a/test_suite/test_bar.py b/test_suite/test_bar.py index 116bcd02..aa219b28 100644 --- a/test_suite/test_bar.py +++ b/test_suite/test_bar.py @@ -619,6 +619,19 @@ def testStdWstring(self): self.assertEqual(embedding.testStdWstring(u"a\0b"), u"a\0b") +class TestPyDictPtr(unittest.TestCase): + def testPyDictPtr(self): + dct = { "a": 10, (1, 2): 20, } + self.assertTrue(embedding.testPyDictPtr(dct)) + self.assertEqual(len(dct), 2) + self.assertEqual(dct.get("b"), 30) + self.assertEqual(dct.get((3, 4)), ("c", "d")) + def testMakePyDictPtr(self): + dct = embedding.makePyDictPtr() + self.assertEqual(len(dct), 1) + self.assertEqual(dct.get("a"), "b") + + import sys test = unittest.defaultTestLoader.loadTestsFromModule(sys.modules[__name__]) testRunner = unittest.TextTestRunner(verbosity = 2)