Simple pybind11 module fails with No module named - python

I've created a python binding for one of my projects a while back and just now wanted to pick it up again.
The binding was no longer working as python was no longer able to import it - this all was working fine back then.
I've then decided to break it down to the simplest possible example:
binding.cpp
#include <pybind11/pybind11.h>
int add(int i, int j) {
return i + j;
}
PYBIND11_MODULE(TestBinding, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("add", &add, "A function which adds two numbers");
}
CMakeLists.txt:
cmake_minimum_required( VERSION 3.2 )
project(TestBinding)
add_subdirectory(pybind11) # or find_package(pybind11)
pybind11_add_module(TestBinding binding.cpp)
# Configure project to inject source path as include directory on dependent projects
target_include_directories( TestBinding
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/pybind11/include/> )
set_target_properties( TestBinding
PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
PREFIX ""
SUFFIX ".so"
)
Then I have a very simple test.py file which goes like this:
sys.path.insert(0, "/path/to/so/lib/")
from TestBinding import *
...which once executed always gives me the following error:
from TestBinding import *
ModuleNotFoundError: No module named 'TestBinding'
I have literally no idea anymore what in the world could have changed from when it worked just fine and now.
Here are some more informations about my working environment:
Windows 10
Visual Studio 15 2017 Win64
Python 3.7 (also tried 3.5 and 3.6)
Am I missing anything really obvious?

I've been able to resolve this by removing the SUFFIX ".so" rule from my CMakeLists.txt.
This was needed back when I've initially created my bindings, but it no longer is apparently.

I have the same problem as you. After checking, it is found that the problem is caused by the inconsistency between the python version of pybind11 and the python version of the local environment. My problem was solved when I adjusted to the same python version.

Related

Call a C# DLL function in Python 3.6

I have this part of a C# DLL from a larger project. It has no dependencies, runs in either .NET 5.0 or .NET 6.0 C# without anything else in it. It looks something like this:
namespace Some.Namespace {
public static class Rename{
public static string RenameString(string stringToRename){
//Do some stuff to it
return stringToRename;
}
}
}
However, no matter what way we attempt to add the reference to Python 3.6 (ArcGIS Pro Distribution) using Python.NET...
sys.path.append(os.path.join(os.getcwd(),"Project_Folder","bin"))
clr.AddReference("SomeDLL")
from Some.Namespace import Rename
It throws this error on the 3rd line.
Exception has occurred: ModuleNotFoundError No module named Some
We've tried just about every possible means to load the DLL at this point and none of them have worked (Yes I know that 2.5.x Python.NET doesn't support .NET 6.0 - we switched to 5.0 and it didn't work either) Exposing the function through Robert's DllExport using ctypes throws a CLR Exception that we can't debug because it's cross-environment whenever the function is run.
Here's that attempt.
//The DLL With Robert's DLLExport
namespace Some.Namespace {
public static class Rename{
//We've also tried CallingConvention.Cdecl
[DllExport("Rename", System.Runtime.InteropServices.CallingConvention.StdCall)]
public static string RenameString(string stringToRename){
//Do some stuff to it
return stringToRename;
}
}
}
#In Python
#We've tried WINDLL
dll_utils = CDLL(os.path.join(os.getcwd(),"project_folder","bin","SomeDLL.dll"))
#We've tried using CFUNCTTYPE here too
encrypt_proto = ctypes.WINFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
encrypt_args = (1, "p1", 0),(1, "p2", 0),
#It fails here when we add the encrypt_args flags to it, saying they don't match up,
#but there's no way to figure out how many it wants, we've tried many variations of
#the above
encrypt_call = encrypt_proto(("Rename",dll_utils),encrypt_args)'
#So because of that on one attempt we removed these two lines
p2 = ctypes.c_char_p(text_to_obfuscate)
p1 = ctypes.c_char_p(text_to_obfuscate)
#And the second argument here
return_value = encrypt_call(p1,p2)
#And...
OSError: [WinError -532462766] Windows Error 0xe0434352
Attempting to write a second function in the DLL to convert the C# string to a byte array and back and using a ctypes pointer doesn't work - it throws the same error just posted when it's called. Someone suggested in another question here IronPython next - which we want to avoid, or to try to use comtypes which I've never used before. At one point we even tried to do a COM decoration and it claimed something I've seen on an answer here is obsolete. I've researched this for 2-3 days and haven't been able to solve this.
At this point I'm at a loss. Is there any effective, simple way, to get an external .NET C# function in a DLL to call a function and pass a string in Python without installing any major dependencies? Just out of the box? There's gotta be something simple I'm missing.

Unable to use pip modules with PyO3

Recently I have been working on a project that involves generating docx files. Since Rust's docx support is still quite immature, I've decided to use Python's python-docx module via PyO3.
Here's my code so far:
extern crate pyo3;
use pyo3::prelude::*;
(...)
// Initialize some Python
let gil = Python::acquire_gil();
let py = gil.python();
let docx = PyModule::import(py, "docx")?;
let document = docx.Document();
Unfortunately, I'm running into two pretty serious errors.
Error #1:
let docx = PyModule::import(py, "docx")?;
^ cannot use the `?` operator in a function that returns `std::string::String
Error #2:
let document = docx.Document();
^^^^^^^^ method not found in `&pyo3::prelude::PyModule`
How do I solve these errors?
N.B. Yes, I have made sure that python-docx is installed. It's located in /home/<my username>/.local/lib/python3.8/site-packages

PythonKit crashes on start due to dlopen returning nil

I'm trying to use some Python Code in my Swift project with PythonKit. In order to do that, I've downloaded the new Xcode 11 to add PythonKit as Swift Package.
After adding PythonKit with Swift Package, I have these project dependencies right here.
I want to use my Python code at the start of my app, so I put the call for Python code in my App Delegate, in the application function.
import UIKit
import PythonKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let sys = Python.import("sys")
print("Python Version: \(sys.version)")
sys.path.append("/Users/me/Documents/MyProject/MyPackage/my_python_main_file")
let pythonAPI = Python.import("my_python_main_file")
pythonAPI.main_function.call()
return true
}
After running my project, I get the following error :
Fatal error: Python library not found. Set the PYTHON_LIBRARY environment variable with the path to a Python library.
I tried to follow the different steps with Breakpoints to know where the code actually crashes. So here is the different stages the PythonKit goes through :
It goes inside PythonLibrary init() and produces the error :
guard let pythonLibraryHandle = PythonLibrary.loadPythonLibrary() else {
fatalError("""
Python library not found. Set the \(Environment.library.key) \
environment variable with the path to a Python library.
""")
}
After investigation, it is because of the call of dlopen inside of loadPythonLibrary function
static func loadPythonLibrary(at path: String) -> UnsafeMutableRawPointer? {
log("Trying to load library at '\(path)'...")
#if canImport(Darwin) || canImport(Glibc)
// Must be RTLD_GLOBAL because subsequent .so files from the imported python
// modules may depend on this .so file.
let pythonLibraryHandle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL)
#elseif os(Windows)
let pythonLibraryHandle = UnsafeMutableRawPointer(LoadLibraryA(path))
#endif
if pythonLibraryHandle != nil {
log("Library at '\(path)' was sucessfully loaded.")
}
return pythonLibraryHandle
}
This functions returns nil, which is due to dlopen function returning nil.
However, I checked the path given as parameter to loadPythonLibrary(at : path) and it appeared to be correct (in Terminal I tried to do cd to the following path and it worked) :
/usr/local/Frameworks/Python.framework/Versions/2.7/Python
I am using Python 2.7 in my code.
Do you know why dlopen would return nil in my case ?
You can do it now using my fork of PythonKit at https://github.com/kewlbear/PythonKit. This package depends on my other package https://github.com/kewlbear/Python-iOS. Ultimately Python will be embedded in the app. Currently Python 3.8 is used.

Wrapping c++ functions in python with ctypes on windows : function not found

I need to run a series of python scripts calculating various scripts, that are working fine, but one of them runs very slowly and has to be done in C++.
The C++ code is ready, but I need to find a way to call the C++ function from the python and get the return value.
I found information about SWIG, but didn't get it to work on Windows with Visual Studio (I have to do it in VS on Windows, because of other constraints). I found ctypes much easier for my very simple function with standart input and output values.
So I did many tests with ctypes, tried all the examples I could find online, and each and every time, what happens is that I can load the dll (built with visual studio 2012 compiler) but when I try calling the function in python, it just goes :
Traceback (most recent call last):
File "test.py", line 3, in <module>
print maLib.plop()
File "C:\Python27\lib\ctypes\__init__.py", line 378, in __getattr__
func = self.__getitem__(name)
File "C:\Python27\lib\ctypes\__init__.py", line 383, in __getitem__
func = self._FuncPtr((name_or_ordinal, self))
AttributeError: function 'plop' not found
The python code is (test.py) :
from ctypes import cdll
lib = cdll.LoadLibrary('test')
print lib.plop()
The C++ code is (test.cpp, compiled as test.dll, Visual Studio set to build as dll) :
extern "C"
{
int plop();
}
int plop() { return 4; }
As you can see, I tried to make it as simple as possible, to avoid details making it fail. I have read the python ctypes help, and tutorials on how to use ctypes, trying exactly the same codes as them, but I had to adapt a bit because I am using Visual Studio / windows and most of the other users are using linux.
All of the files are in the same folder.
I have tried multiple ways of loading the library : LoadLibrary('./name.dll'), WinDLL('name.dll'), giving the function a different name, void return type, etc...
Do you think I should use SWIG ? Is my problem an obvious beginner mistake ? I am a student, and new to most of what I'm using, but I put a lot of effort in this, even if I just need it for a particular single task.
When using SWIG, I had to make a wrapper with function pointers, or references, so I thought this was the problem, which made me want to try a simpler solution.
I am using : Python 2.7.7, Visual Studio 2012, Windows 8.1
Many thanks in advance
The error reported by your Python is very clear.
function 'plop' not found
This means that the DLL does not export a function of that name. So, you need to export the function. Either with a .def file, or using __declspec(dllexport):
extern "C"
{
__declspec(dllexport) int plop();
}
To inspect your DLL to debug issues like this, use dumpbin or Dependency Walker.
Note that since you do not specify calling convention, the default of __cdecl is used. That means that cdll is correct on the ctypes side.
You need to give your function "C" linkage to avoid name mangling. Declare it like this:
extern "C"
{
int plop();
}
and it will then properly be called "plop" rather than being name-mangled.

Calling PARI/GP from Python

I would like to call PARI/GP from Python only to calculate the function nextprime(n) for different ns that I define. Unfortunately I can't get pari-python to install so I thought I would just call it using a command line via os.system in Python. I can't see in the man page how to do get PARI/GP to run in non-interactive mode, however. Is there a way to achieve this?
You can pipe input into gp's stdin like so, using the -q flag to quash verbosity:
senderle:~ $ echo "print(isprime(5))" | gp -q
1
However, it's not much harder to create a simple python extension that allows you to pass strings to pari's internal parser and get results back (as strings). Here's a bare-bones version that I wrote some time ago so that I could call pari's implementation of the APRT test from python. You could extend this further to do appropriate conversions and so on.
//pariparse.c
#include<Python.h>
#include<pari/pari.h>
static PyObject * pariparse_run(PyObject *self, PyObject *args) {
pari_init(40000000, 2);
const char *pari_code;
char *outstr;
if (!PyArg_ParseTuple(args, "s", &pari_code)) { return NULL; }
outstr = GENtostr(gp_read_str(pari_code));
pari_close();
return Py_BuildValue("s", outstr);
}
static PyMethodDef PariparseMethods[] = {
{"run", pariparse_run, METH_VARARGS, "Run a pari command."},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC initpariparse(void) {
(void) Py_InitModule("pariparse", PariparseMethods);
}
And the setup file:
#setup.py
from distutils.core import setup, Extension
module1 = Extension('pariparse',
include_dirs = ['/usr/include', '/usr/local/include'],
libraries = ['pari'],
library_dirs = ['/usr/lib', '/usr/local/lib'],
sources = ['pariparse.c'])
setup (name = 'pariparse',
version = '0.01a',
description = 'A super tiny python-pari interface',
ext_modules = [module1])
Then just type python setup.py build to build the extension. You can then call it like this:
>>> pariparse.run('nextprime(5280)')
'5281'
I tested this just now and it compiled for me with the latest version of pari available via homebrew (on OS X). YMMV!
You might want to try using the Sage math tool. Sage uses Python to glue together all sorts of math libraries, including PARI. Some of the math libraries are nicely integrated, others use hacks (passing strings in to the library and then parsing out the string results) but in all cases someone else did the integration work for you and you can just use it.
You can set up your own Sage system, or you can get a free account and try Sage on the University of Washington servers.
I don't think it is a good idea to call os.system except for a quick and dirty workaround when you have a reliable C library behind it. It is very easy to call C functions from Python; here are two functions for calling nextprime. One is using long integers (despite the name, it will mean here that you are using small integer numbers); the other is using the string type (for longer integers).
First check that you have the libpari installed. The solution below is for Linux and assumes that your library is called libpari.so. Under Windows it will probably be called with a .dll suffix instead. You may have to type the whole path of the DLL file if it isn't found at first attempt:
import ctypes
# load the library
pari=ctypes.cdll.LoadLibrary("libpari.so")
# set the right return type of the functions
pari.stoi.restype = ctypes.POINTER(ctypes.c_long)
pari.nextprime.restype = ctypes.POINTER(ctypes.c_long)
pari.strtoGENstr.restype = ctypes.POINTER(ctypes.c_long)
pari.geval.restype = ctypes.POINTER(ctypes.c_long)
pari.itostr.restype = ctypes.c_char_p
# initialize the library
pari.pari_init(2**19,0)
def nextprime(v):
g = pari.nextprime(pari.stoi(ctypes.c_long(v)))
return pari.itos(g)
def nextprime2(v):
g = pari.nextprime(pari.geval(pari.strtoGENstr(str(v))))
return int(pari.itostr(g))
print( nextprime(456) )
print( nextprime2(456) )

Categories