MeshLib
 
Loading...
Searching...
No Matches
MRPython.h
Go to the documentation of this file.
1#pragma once
2
3#include "MRMeshFwd.h"
4#if !defined( __EMSCRIPTEN__) && !defined( MRMESH_NO_PYTHON )
5
6#if defined( __clang__ )
7#pragma clang diagnostic push
8#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
9#endif
10#include <pybind11/pybind11.h>
11#include <pybind11/operators.h>
12#include <pybind11/stl_bind.h>
13#include <pybind11/numpy.h>
14#if defined( __clang__ )
15#pragma clang diagnostic pop
16#endif
17
18#include "MRExpected.h"
19#include <functional>
20#include <filesystem>
21#include <unordered_map>
22
23#define MR_INIT_PYTHON_MODULE( moduleName ) MR_INIT_PYTHON_MODULE_PRECALL( moduleName, [](){} )
24
25#define MR_INIT_PYTHON_MODULE_PRECALL( moduleName, precall )\
26PYBIND11_MODULE( moduleName, m )\
27{\
28 precall();\
29 auto& adders = MR::PythonExport::instance().functions( #moduleName );\
30 for ( auto& fs : adders )\
31 for ( auto& f : fs )\
32 f( m );\
33}\
34static MR::PythonFunctionAdder moduleName##_init_( #moduleName, &PyInit_##moduleName );
35
36#define MR_ADD_PYTHON_FUNCTION( moduleName , name , func , description ) \
37 static MR::PythonFunctionAdder name##_adder_( #moduleName, [](pybind11::module_& m){ m.def(#name, func, description);} );
38
39#define MR_ADD_PYTHON_CUSTOM_DEF( moduleName , name , ... ) \
40_Pragma("warning(push)") \
41_Pragma("warning(disable:4459)") \
42 static MR::PythonFunctionAdder name##_adder_( #moduleName, __VA_ARGS__ ); \
43_Pragma("warning(pop)")
44
46#define _MR_PYTHON_CUSTOM_CLASS_HOLDER_NAME( name ) name##_class_
47
50#define MR_PYTHON_CUSTOM_CLASS( name ) ( *_MR_PYTHON_CUSTOM_CLASS_HOLDER_NAME( name ) )
51
54#define MR_ADD_PYTHON_CUSTOM_CLASS_DECL( moduleName, name, ... ) \
55static std::optional<pybind11::class_<__VA_ARGS__>> _MR_PYTHON_CUSTOM_CLASS_HOLDER_NAME( name );
56
59#define MR_ADD_PYTHON_CUSTOM_CLASS_INST( moduleName, name ) \
60MR_ADD_PYTHON_CUSTOM_DEF( moduleName, name##_inst_, [] ( pybind11::module_& module ) \
61{ \
62 _MR_PYTHON_CUSTOM_CLASS_HOLDER_NAME( name ).emplace( module, #name ); \
63}, MR::PythonExport::Priority::Declaration )
64
70#define MR_ADD_PYTHON_CUSTOM_CLASS_INST_FUNC( moduleName, name, ... ) \
71MR_ADD_PYTHON_CUSTOM_DEF( moduleName, name##_inst_, [] ( pybind11::module_& module ) \
72{ \
73 _MR_PYTHON_CUSTOM_CLASS_HOLDER_NAME( name ) = __VA_ARGS__ ( module ); \
74}, MR::PythonExport::Priority::Declaration )
75
96#define MR_ADD_PYTHON_CUSTOM_CLASS( moduleName, name, ... ) \
97MR_ADD_PYTHON_CUSTOM_CLASS_DECL( moduleName, name, __VA_ARGS__ ) \
98MR_ADD_PYTHON_CUSTOM_CLASS_INST( moduleName, name )
99
100#define MR_ADD_PYTHON_VEC( moduleName, name, type) \
101PYBIND11_MAKE_OPAQUE( std::vector<type> ) \
102MR_ADD_PYTHON_CUSTOM_CLASS_DECL( moduleName, name, std::vector<type>, std::unique_ptr<std::vector<type>> ) \
103MR_ADD_PYTHON_CUSTOM_CLASS_INST_FUNC( moduleName, name, [] ( pybind11::module_& module ) { return pybind11::bind_vector<std::vector<type>>( module, #name, pybind11::module_local(false) ); } ) \
104MR_ADD_PYTHON_CUSTOM_DEF( moduleName, name, [] ( pybind11::module_& ) \
105{\
106 using vecType = std::vector<type>;\
107 MR_PYTHON_CUSTOM_CLASS( name ).\
108 def( pybind11::init<>() ).\
109 def( pybind11::init<size_t>(), pybind11::arg( "size" ) ).\
110 def( "empty", &vecType::empty ).\
111 def( "size", &vecType::size ).\
112 def( "resize", ( void ( vecType::* )( const vecType::size_type ) )& vecType::resize ).\
113 def( "clear", &vecType::clear ); \
114} )
115
116#define MR_ADD_PYTHON_MAP( moduleName, name, mapType ) \
117PYBIND11_MAKE_OPAQUE( mapType ) \
118MR_ADD_PYTHON_CUSTOM_CLASS_DECL( moduleName, name, mapType, std::unique_ptr<mapType> ) \
119MR_ADD_PYTHON_CUSTOM_CLASS_INST_FUNC( moduleName, name, [] ( pybind11::module_& module ) { return pybind11::bind_map<mapType>( module, #name, pybind11::module_local(false) ); } ) \
120MR_ADD_PYTHON_CUSTOM_DEF( moduleName, name, [] ( pybind11::module_& ) \
121{\
122 MR_PYTHON_CUSTOM_CLASS( name ).\
123 def( pybind11::init<>() ).\
124 def( "size", &mapType::size );\
125} )
126
127#define MR_ADD_PYTHON_EXPECTED( moduleName, name, type, errorType ) \
128using name##_expected_type_ = MR::Expected<type, errorType>; \
129MR_ADD_PYTHON_CUSTOM_CLASS( moduleName, name, name##_expected_type_ ) \
130MR_ADD_PYTHON_CUSTOM_DEF( moduleName, name, [] ( pybind11::module_& ) \
131{\
132 using expectedType = Expected<type,errorType>;\
133 MR_PYTHON_CUSTOM_CLASS( name ).\
134 def( "has_value", []() \
135 { PyErr_WarnEx(PyExc_DeprecationWarning, ".has_value is deprecated. Please use 'try - except ValueError'", 1); \
136 return &expectedType::has_value; \
137 }).\
138 def( "value", []() \
139 { \
140 PyErr_WarnEx(PyExc_DeprecationWarning, ".value is deprecated. Please use 'try - except ValueError'", 1); \
141 return ( type& ( expectedType::* )( )& )& expectedType::value; \
142 }, pybind11::return_value_policy::reference_internal ).\
143 def( "error", []() \
144 { \
145 PyErr_WarnEx(PyExc_DeprecationWarning, ".error is deprecated. Please use 'try - except ValueError'", 1); \
146 return ( const errorType& ( expectedType::* )( )const& )& expectedType::error; \
147 } );\
148} )
149
155
156template<StreamType T>
158{
159public:
160 MRMESH_API void write( const std::string& text );
161 void flush() {}
163};
164
167
168namespace MR
169{
170
172{
173public:
175
176 using PythonRegisterFuncton = std::function<void( pybind11::module_& m )>;
177
178 enum class Priority
179 {
182 Count,
183 };
184
186 {
187 PyObject* ( *initFncPointer )( void );
188 std::array<std::vector<PythonRegisterFuncton>, size_t( Priority::Count )> functions;
189 };
190
191 void addFunc( const std::string& moduleName, PythonRegisterFuncton func, Priority priority )
192 {
193 auto& mod = moduleData_[moduleName];
194 mod.functions[size_t( priority )].push_back( func );
195 }
196 void setInitFuncPtr( const std::string& moduleName, PyObject* ( *initFncPointer )( void ) )
197 {
198 auto& mod = moduleData_[moduleName];
199 mod.initFncPointer = initFncPointer;
200 }
201 const std::array<std::vector<PythonRegisterFuncton>, size_t( Priority::Count )>& functions( const std::string& moduleName ) const
202 {
203 auto it = moduleData_.find( moduleName );
204 const static std::array<std::vector<PythonRegisterFuncton>, size_t( Priority::Count )> empty;
205 if ( it == moduleData_.end() )
206 return empty;
207 return it->second.functions;
208 }
209
210 const std::unordered_map<std::string, ModuleData>& modules() const
211 {
212 return moduleData_;
213 }
214private:
215 PythonExport() = default;
216 ~PythonExport() = default;
217
218 std::unordered_map<std::string, ModuleData> moduleData_;
219};
220
222{
223 MRMESH_API PythonFunctionAdder( const std::string& moduleName, std::function<void( pybind11::module_& m )> func, PythonExport::Priority priority = PythonExport::Priority::Implementation );
224 MRMESH_API PythonFunctionAdder( const std::string& moduleName, PyObject* ( *initFncPointer )( void ) );
225};
226
227// overload `toString` functoion to throw exception from custom `Expected::error` type
228template<typename E>
230{
231 if constexpr (std::is_nothrow_convertible<E, std::string>::value)
232 throw std::runtime_error(err);
233 else
234 throw std::runtime_error(toString(err));
235}
236
237template<typename R, typename E, typename... Args>
238auto decorateExpected( std::function<Expected<R, E>( Args... )>&& f ) -> std::function<R( Args... )>
239{
240 return[fLocal = std::move( f )]( Args&&... args ) mutable -> R
241 {
242 auto res = fLocal(std::forward<Args>( args )...);
243 if (!res.has_value())
244 throwExceptionFromExpected(res.error());
245
246 if constexpr (std::is_void<R>::value)
247 return;
248 else
249 return res.value();
250 };
251}
252
253template<typename F>
254auto decorateExpected( F&& f )
255{
256 return decorateExpected( std::function( std::forward<F>( f ) ) );
257}
258
259template<typename R, typename T, typename... Args>
260auto decorateExpected( R( T::* memFunction )( Args... ) )
261{
262 return decorateExpected( std::function<R( T*, Args... )>( std::mem_fn( memFunction ) ) );
263}
264
265}
266#endif
#define MRMESH_API
Definition MRMesh/MRMeshFwd.h:46
StreamType
Definition MRPython.h:151
@ Stderr
Definition MRPython.h:153
@ Stdout
Definition MRPython.h:152
Definition MRPython.h:172
std::function< void(pybind11::module_ &m)> PythonRegisterFuncton
Definition MRPython.h:176
void setInitFuncPtr(const std::string &moduleName, PyObject *(*initFncPointer)(void))
Definition MRPython.h:196
const std::array< std::vector< PythonRegisterFuncton >, size_t(Priority::Count)> & functions(const std::string &moduleName) const
Definition MRPython.h:201
Priority
Definition MRPython.h:179
void addFunc(const std::string &moduleName, PythonRegisterFuncton func, Priority priority)
Definition MRPython.h:191
const std::unordered_map< std::string, ModuleData > & modules() const
Definition MRPython.h:210
static MRMESH_API PythonExport & instance()
Definition MRPython.h:158
static MRMESH_API int getNumWritten()
MRMESH_API void write(const std::string &text)
void flush()
Definition MRPython.h:161
Definition MRCameraOrientationPlugin.h:7
void throwExceptionFromExpected(const E &err)
Definition MRPython.h:229
tl::expected< T, E > Expected
Definition MRExpected.h:49
auto decorateExpected(std::function< Expected< R, E >(Args...)> &&f) -> std::function< R(Args...)>
Definition MRPython.h:238
MRMESH_API std::string_view toString(DimensionsVisualizePropertyType value)
Definition MRPython.h:186
std::array< std::vector< PythonRegisterFuncton >, size_t(Priority::Count)> functions
Definition MRPython.h:188
Definition MRPython.h:222
MRMESH_API PythonFunctionAdder(const std::string &moduleName, PyObject *(*initFncPointer)(void))
MRMESH_API PythonFunctionAdder(const std::string &moduleName, std::function< void(pybind11::module_ &m)> func, PythonExport::Priority priority=PythonExport::Priority::Implementation)