Dune-Fufem 2.11-git
Loading...
Searching...
No Matches
common.hh
Go to the documentation of this file.
1// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set ts=8 sw=4 et sts=4:
3
4// SPDX-FileCopyrightText: Copyright © DUNE-FUFEM Project contributors, see file AUTHORS.md
5// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later
6
7#ifndef DUNE_FUFEM_PYTHON_COMMON_HH
8#define DUNE_FUFEM_PYTHON_COMMON_HH
9
10// Only introduce the dune-python interface if python
11// was found and enabled.
12#if HAVE_PYTHON || DOXYGEN
13
14#include <Python.h>
15
16#include <string>
17#include <sstream>
18#include <type_traits>
19#include <tuple>
20
22
25
29
30
31namespace Python
32{
33
34// Forward declarations
35bool isModule(const Reference&);
36
37
38
39
40
41// *****************************************************************************
42// Base class of Conversion that do the PyObject* <-> C++-type conversion
43// *****************************************************************************
44
64template<class T>
66{
67 static void toC(PyObject* x, T& y)
68 {
69 DUNE_THROW(Dune::Exception, "Conversion to C type " << typeid(T).name() << " not implemented");
70 }
71
72 static PyObject* toPy(const T& x)
73 {
74 DUNE_THROW(Dune::Exception, "Conversion from C type " << typeid(T).name() << " not implemented");
75 return 0;
76 }
77};
78
79
80
81// *****************************************************************************
82// The following are helper functions for implementors and should not be
83// used by user code in order to avoid messing up reference counts.
84// *****************************************************************************
85namespace Imp
86{
87 PyObject* inc(PyObject* p)
88 {
89 Py_XINCREF(p);
90 return p;
91 }
92
93 Reference& inc(Reference& p)
94 {
95 Py_XINCREF(p);
96 return p;
97 }
98
99 // Calling makeObject() with a type derived from Reference should
100 // just return a Reference to the same object.
101 template<class T>
103 makeObject(const T& t)
104 {
105 return t;
106 }
107
108 // Calling makeObject() with any type not derived from Reference should
109 // convert this object to a Reference.
110 template<class T>
111 typename std::enable_if<not(std::is_base_of<Reference,T>::value), Reference>::type
112 makeObject(const T& t)
113 {
114 return Conversion<T>::toPy(t);
115 }
116
117
118} // end of namespace Imp
119
120
121// *****************************************************************************
122// The following methods simplify the usage of the python api from C++.
123// *****************************************************************************
124
130void start()
131{
132 // initialize without registering signal handlers
133 // otherwise you cannot stop the program with ctrl-c
134 Py_InitializeEx(0);
135}
136
143void stop()
144{
145 Py_Finalize();
146}
147
160Module import(const std::string& moduleName)
161{
162 Module pModule = PyImport_ImportModule(moduleName.c_str());
163 if (not pModule)
164 DUNE_THROW(Dune::Exception, "failed to retrieve python module " << moduleName);
165 return pModule;
166}
167
183{
184 // PyImport_AddModule returns a borrowed reference so we have to call inc.
185 Module pModule = Imp::inc(PyImport_AddModule(moduleName.c_str()));
186 if (not pModule)
187 DUNE_THROW(Dune::Exception, "failed to create python module " << moduleName);
188#if PY_MAJOR_VERSION >= 3
189 Reference pBuiltin = Python::import("builtins");
190#else
191 Reference pBuiltin = Python::import("__builtin__");
192#endif
193 if (pBuiltin)
194 pModule.set("__builtins__", pBuiltin);
195 return pModule;
196}
197
206{
207 return import("__main__");
208}
209
223void run(const std::string& code)
224{
225 PyRun_SimpleString(code.c_str());
226}
227
239void run(const std::string& code, Reference& module)
240{
241 if (not isModule(module))
242 DUNE_THROW(Dune::Exception, "tried to use non-module where a module is expected");
243 Python::Reference dict = module.get("__dict__");
244 PyRun_String(code.c_str(), Py_file_input, dict, dict);
245}
246
274
287{
288 return module.runStream();
289}
290
299void runFile(const std::string& fileName)
300{
301 // the last argument specifies that the file should be closed
302 // after executing the code
303 PyRun_SimpleFileEx(fopen(fileName.c_str(), "r"),fileName.c_str(), 1);
304}
305
317void runFile(const std::string& fileName, Module module)
318{
319 module.runFile(fileName);
320}
321
332{
333 Module main = Python::import("__main__");
334 return main.evaluate(expression);
335}
336
337Reference evaluate(const std::string& expression, Module module)
338{
339 return module.evaluate(expression);
340}
341
350template<class T>
352{
353 return Imp::makeObject(t);
354}
355
363template<typename... Ts>
364Reference makeTuple(const Ts&... ts)
365{
366 return PyTuple_Pack(sizeof...(Ts), (static_cast<PyObject*>(makeObject(ts)))...);
367}
368
369template<typename... Ts>
370Reference tuple(const Ts&... ts)
371{
372 return makeTuple(ts...);
373}
374
381{
382 return PyDict_New();
383}
384
391int size(const Reference& s)
392{
393 return PySequence_Size(s);
394}
395
407{
408 return PyObject_GetIter(seq);
409}
410
422{
423 return PyIter_Next(iter);
424}
425
437Reference getItem(const Reference& seq, int i)
438{
439 return PySequence_GetItem(seq, i);
440}
441
454{
455 // We have to use inc() since PyDict_GetItem returns a borrowed reference!
456 return Reference(Imp::inc(PyDict_GetItem(dict, k)));
457}
458
470template<class V>
471void setItem(Reference& seq, int i, const V& v)
472{
473 PySequence_SetItem(seq, i, makeObject(v));
474}
475
487template<class K, class V>
488void setItem(Reference& dict, const K& k, const V& v)
489{
490 PyDict_SetItem(dict, makeObject(k), makeObject(v));
491}
492
499bool isSequence(const Reference& ref)
500{
501 return PySequence_Check(ref);
502}
503
510bool isDict(const Reference& ref)
511{
512 return PyDict_Check(ref);
513}
514
521bool isModule(const Reference& ref)
522{
523 return ref && PyModule_Check(ref);
524}
525
532bool isCallable(const Reference& ref)
533{
534 return ref && PyCallable_Check(ref);
535}
536
544{
545 return PyDict_Keys(dict);
546}
547
553template<class Signature, template<class> class DerivativeTraits = Dune::Functions::DefaultDerivativeTraits>
555{
558 return [callable = Python::Callable(f)](const Domain& x) {
559 return callable(x).template toC<Range>();
560 };
561}
562
571template<class Signature, template<class> class DerivativeTraits = Dune::Functions::DefaultDerivativeTraits, class... R>
572auto makeDifferentiableFunction(const R&... f)
573{
575 auto derivativeSignatureTags = Dune::Functions::derivativeSignatureTags<sizeof...(f)-1>(signatureTag);
576
577 return std::apply([&f..., &signatureTag](auto... tag) {
578 return Dune::Functions::makeDifferentiableFunctionFromCallables(signatureTag,
579 makeFunction<typename decltype(tag)::Signature>(f)...);
580 }, derivativeSignatureTags);
581}
582
590{
591 PyObject *rawType, *rawValue, *rawTraceback;
592 PyErr_Fetch(&rawType, &rawValue, &rawTraceback);
593
594 Reference type(rawType);
595 Reference value(rawValue);
596 Reference traceback(rawTraceback);
597 if (type and value)
599 "Python error occurred." << std::endl <<
600 " Origin: " << origin << std::endl <<
601 " Error: " << message << std::endl <<
602 " Python error message: " << value.str() );
603 else if (type)
605 "Python error occurred." << std::endl <<
606 " Origin: " << origin << std::endl <<
607 " Error: " << message);
608}
609
610
611} // end of namespace Python
612
613
614
615#else
616 #warning dunepython.hh was included but python was not found or enabled!
617#endif // DUNE_FUFEM_PYTHON_COMMON_HH
618
619
620#endif
621
const char * name()
auto derivativeSignatureTags(Dune::Functions::SignatureTag< Signature, DerivativeTraits > tag)
int size() const
void message(const std::string &msg)
#define DUNE_THROW(E,...)
Definition callable.hh:34
void run(const std::string &code)
Run python code given as string.
Definition common.hh:223
Reference iter(const Reference &seq)
Get iterator of iterable object.
Definition common.hh:406
Module import(const std::string &moduleName)
Import python module given by name.
Definition common.hh:160
Reference keys(const Reference &dict)
Get keys of python dictionary.
Definition common.hh:543
Module::AutoRunCodeStream runStream()
Obtain a stream to feed the code with multiple lines of python code.
Definition common.hh:270
bool isCallable(const Reference &ref)
Check if python object is callable.
Definition common.hh:532
void handlePythonError(const std::string &origin, const std::string &message)
If a python error occurred throw an exception and clear python error indicator.
Definition common.hh:589
bool isSequence(const Reference &ref)
Check if python object is some sequence.
Definition common.hh:499
void runFile(const std::string &fileName)
Run python code in file given by name.
Definition common.hh:299
Reference tuple(const Ts &... ts)
Definition common.hh:370
Module createModule(const std::string &moduleName)
Create python module given by name.
Definition common.hh:182
Reference makeTuple(const Ts &... ts)
create a Python tuple
Definition common.hh:364
bool isDict(const Reference &ref)
Check if python object is a dictionary.
Definition common.hh:510
Reference makeObject(const T &t)
Create python object from C++ object.
Definition common.hh:351
bool isModule(const Reference &)
Check if python object is a module.
Definition common.hh:521
void setItem(Reference &seq, int i, const V &v)
Set item of sequence.
Definition common.hh:471
Module main()
Obtain the main module.
Definition common.hh:205
Reference getItem(const Reference &seq, int i)
Get item of sequence.
Definition common.hh:437
Reference next(const Reference &iter)
Get next item from iterator.
Definition common.hh:421
Reference evaluate(const std::string &expression)
Evaluate python expression given as string.
Definition common.hh:331
auto makeDifferentiableFunction(const R &... f)
Create a callable differentiable function for given signature.
Definition common.hh:572
auto makeFunction(const Reference &f)
Create a callable function for given signature.
Definition common.hh:554
Reference dict()
Create an empty Python tuple.
Definition common.hh:380
void start()
Start embedded python interpreter.
Definition common.hh:130
void stop()
Stop embedded python interpreter.
Definition common.hh:143
Wrapper class for callable python objects.
Definition callable.hh:174
Conversion of C type T from and to PyObject*.
Definition common.hh:66
static PyObject * toPy(const T &x)
Definition common.hh:72
static void toC(PyObject *x, T &y)
Definition common.hh:67
Wrapper class for python modules.
Definition module.hh:50
Reference evaluate(const std::string &expression)
Evaluate python expression given as string.
Definition module.hh:245
A stream that executed all python code it is fed with on destruction.
Definition module.hh:61
Wrapper class for python objects.
Definition reference.hh:77
void set(const std::string &name, const V &value)
Set attribute of given name.
Definition reference.hh:258
T apply(T... args)
T c_str(T... args)
T endl(T... args)