Building Python extension module with distutils - c++

I'm using distutils to build a Python extension module written in C++. The problem I have is that in order to compile the extension module, I need to link with a certain shared library. This requires setting an additional compiler flag. So, I searched through the Python docs and found out about the extra_compile_args property of the Extension object. So I tried the following:
from distutils.core import setup, Extension
module = Extension('test', sources = ['test.cpp'])
module.extra_compile_args = ['--std=c++0x', '-l mylib'];
setup(name = 'test', version = '1.0', ext_modules = [module])
This seems to compile, except when I import my module in Python it throws an ImportError exception due to an undefined symbol. So, apparently the library didn't link properly. So I tried writing a throw away C++ program which linked with the shared library, and it ran fine. Then I realized something really odd is going on with distutils, because if I add a compile argument that links to a bogus library name, distutils just compiles everything with no problem:
module.extra_compile_args = ['--std=c++0x', '-l some_fake_library'];
When I run setup.py build, the build runs with no errors!
So, what's going on here? How can I compile an extension module that requires linkage to a shared library?

There's actually a special option for that.
For example:
libraries=["rt"]
You leave off the option and lib parts.

One of the purposes of distutils is to make your code not dependent on a single compiler. Your "-l somelib" looks like it's meant to work with GCC (even though it should be "-lsomelib", no space). This is why you use the libraries option to the Extension class. Distutils will then pass the appropriate link command to whatever compiler it's using.
You can also look at the actual build commands distutils is using and execute them yourself to see what is going wrong.

Related

Tkinter module compilation error - 2.5.2 needed

I have a decently long program that I have been trying to compile. I have tried py2exe and cx_Freeze, both seem to come up with this problem.
I used the following setup.py file to compile my program:
import sys
from cx_Freeze import setup, Executable
base = None
if sys.platform == 'win32':
base = 'Win32GUI'
executables = [
Executable('version_3_2.py', base=base)
]
setup(name='version_3_2',
version='0.32',
description='desc',
executables=executables
)
From running this using
python setup.py build
The executable is created.
From running the executable, i was given a traceback stating that
TclError: Can't find a usable init.tcl in the following directories:
C:/Python27/build/lib/tcl8.5
and a bunch of other directories
From adding all of the tkinter and tcl files and folers into a couple of those directories i get the next traceback from executing:
C:/Python27/build/lib/tcl8.5/init.tcl: version conflict for package "Tcl":
have 8.5.15, need exactly 8.5.2
version conflict for package "Tcl": have 8.5.15, need exactly 8.5.2
while executing
"package require -exact Tcl 8.5.2"
(file "C:/Python27/build/lib/tcl8.5/init.tcl" line 20)
invoked from within
"source C:/Python27/build/lib/tcl8.5/init.tcl"
("uplevel" body line 1)
invoked from within
"uplevel #0 [list source $tclfile]"
I'm not entirely sure what to do. Several solutions like How to correct TCL_LIBRARY and TK_LIBRARY with py2exe and Py2exe with Tkinter have not worked.
Any Ideas?
You need to match exactly the libtcl8.5.dll (or whatever the name is on your system) and the init.tcl (and related files) because they are both part of the same Tcl installation. If you change one out, you must change the other as well. How exactly you've mangled your installation I'm not quite sure, but mangled it surely is. Also be aware that in Tcl 8.5 (and before), it's strongly advised to match the Tk version precisely to it as well; we relax this requirement in 8.6 but you're not using that…
(Note that this is different from the way that normal dependencies work in Tcl; typically a program will depend on a minimum API version, not an exact one.)
Since you've got a binary build of 8.5.15 in use, you might as well use the script files for that version too. I suggest getting the Tcl source distribution for 8.5.15 from SourceForge and grabbing the files out of the library directory in there. You probably only need the .tcl files directly in there (especially init.tcl of course!) and not in any of the subdirectories, as the subdirectories are for official add on packages and those aren't so tightly bound to Tcl versions.

Where Should Shared Object Files Be Placed?

I am venturing into the land of creating C/C++ bindings for Python using pybindgen. I've followed the steps outlined under "Building it ( GCC instructions )" to create bindings for the sample files:
http://packages.python.org/PyBindGen/tutorial.html#a-simple-example
Running make produces a .so file. If I understand how .so files work, I should be able to import the classes in the shared object into Python. However, I'm not sure where to place the file and how to let Python know where it is. Additionally, do the original c/c++ source files need to accompany the .so file?
So far I've tried placing the file in /usr/local/lib and adding that path to DYLD_LIBRARY_PATH to the .bash_profile. When I try to import the module from within the Python interpeter an error is thrown stating that the module can not be found.
So, my question is: What needs to be done with the generated .so file in order for it to be used by a Python program?
Python looks for .so modules in the same directories where it searches python ones. So you have to install it as you would normal python module either somewhere that is on python's sys.path by default (/usr/share/python/site-lib or something like that—it'd distribution-dependent) or add the directory to PYTHONPATH environment variable.
It's python that's loading the module using dlopen, not the dynamic linker, so LD_LIBRARY_PATH (note, there is no DY) won't help you.
Same as all other Python modules. It must be within one of the locations given in sys.path.

Py_Initialize fails - unable to load the file system codec

I am attempting to put together a simple c++ test project that uses an embedded python 3.2 interpreter. The project builds fine but Py_Initialize raises a fatal error:
Fatal Python error: Py_Initialize: unable to load the file system codec
LookupError: no codec search functions registered: can't find encoding
Minimal code:
#include <Python.h>
int main (int, char**)
{
Py_Initialize ();
Py_Finalize ();
return 0;
}
The OS is 32bit Vista.
The python version used is a python 3.2 debug build, built from sources using VC++ 10.
The python_d.exe file from the same build runs without any problems.
Could someone explain the problem and how to fix it? My own google-fu fails me.
EDIT 1
After going through the python source code I've found that, as the error says, no codec search functions have been registered. Both codec_register and PyCodec_Register are as they should be. It's just that nowhere in the code are any of these functions called.
I don't really know what this means as I still have no idea when and from where these functions should have been called. The code that raises the error is entirely missing from the source of my other python build (3.1.3).
EDIT 2
Answered my own question below.
Check the PYTHONPATH and PYTHONHOME environment variables and make sure they don't point to Python 2.x.
http://bugs.python.org/issue11288
Parts of this have been mentioned before, but in a nutshell this is what worked for my environment where I have multiple Python installs and my global OS environment set-up to point to a different install than the one I attempt to work with when encountering the problem.
Make sure your (local or global) environment is fully set-up to point to the install you aim to work with, e.g. you have two (or more) installs of, let's say a python27 and python33 (sorry these are windows paths but the following should be valid for equivalent UNIX-style paths just as well, please let me know about anything I'm missing here (probably the DLLs path might differ)):
C:\python27_x86
C:\python33_x64
Now, if you intend to work with your python33 install but your global environment is pointing to python27, make sure you update your environment as such (while PATH and PYTHONHOME may be optional (e.g. if you temporarily work in a local shell)):
PATH="C:\python33_x64;%PATH%"
PYTHONPATH="C:\python33_x64\DLLs;C:\python33_x64\Lib;C:\python33_x64\Lib\site-packages"
PYTHONHOME=C:\python33_x64
Note, that you might need/want to append any other library paths to your PYTHONPATH if required by your development environment, but having your DLLs, Lib and site-packages properly set-up is of prime importance.
Hope this helps.
The core reason is quite simple: Python does not find its modules directory, so it can of course not load encodings, too
Python doc on embedding says "Py_Initialize() calculates the module search path based upon its best guess" ... "In particular, it looks for a directory named lib/pythonX.Y"
Yet, if the modules are installed in (just) lib - relative to the python binary - above guess is wrong.
Although docs says that PYTHONHOME and PYTHONPATH are regarded, we observed that this was not the case; their actual presence or content was completely irrelevant.
The only thing that had an effect was a call to Py_SetPath() with e.g. [path-to]\lib as argument before Py_Initialize().
Sure this is only an option for an embedding scenario where one has direct access and control over the code; with a ready-made solution, special steps may be necessary to solve the issue.
Ran into the same thing trying to install brew's python3 under Mac OS! The issue here is that in Mac OS, homebrew puts the "real" python a whole layer deeper than you think. You would think from the homebrew output that
$ echo $PYTHONHOME
/usr/local/Cellar/python3/3.6.2/
$ echo $PYTHONPATH
/usr/local/Cellar/python3/3.6.2/bin
would be correct, but invoking $PYTHONPATH/python3 immediately crashes with the abort 6 "can't find encodings." This is because although that $PYTHONHOME looks like a complete installation, having a bin, lib etc, it is NOT the actual Python, which is in a Mac OS "Framework". Do this:
PYTHONHOME=/usr/local/Cellar/python3/3.x.y/Frameworks/Python.framework/Versions/3.x
PYTHONPATH=$PYTHONHOME/bin
(substituting version numbers as appropriate) and it will work fine.
From python3k, the startup need the encodings module, which can be found in PYTHONHOME\Lib directory.
In fact, the API Py_Initialize () do the init and import the encodings module.
Make sure PYTHONHOME\Lib is in sys.path and check the encodings module is there.
I had this issue with python 3.5, anaconda 3, windows 7 32 bit. I solved it by moving my pythonX.lib and pythonX.dll files into my working directory and calling
Py_SetPythonHome(L"C:\\Path\\To\\My\\Python\\Installation");
before initialize so that it could find the headers that it needed, where my path was to "...\Anaconda3\". The extra step of calling Py_SetPythonHome was required for me or else I'd end up getting other strange errors where python import files.
I just ran into the exact same problem (same Python version, OS, code, etc).
You just have to copy Python's Lib/ directory in your program's working directory ( on VC it's the directory where the .vcproj is )
There appears to be something going wrong with the release build either failing to include the appropriate codecs or else misidentifying the codec to use for system APIs. Since the python_d executable is working, what does that return for os.getfsencoding()? (Use the C API to call that between your Initialize/Finalize calls)
I had the same issue and found this question. However from the answers here I was not able to solve my problem. I started debugging the cpython code and thought that I might be discovered a bug. Therefore I opened a issue on the python issue tracker.
My mistake was that I did not understand that Py_SetPath clears all inferred paths.
So one needs to set all paths when calling this function.
Link to the issue conversation
For completion I also copied the most important part of the conversation below.
My original issue text
I compiled the source of CPython 3.7.3 myself on Windows with Visual Studio 2017 together with some packages like e.g numpy. When I start the Python Interpreter I am able to import and use numpy. However when I am running the same script via the C-API I get an ModuleNotFoundError.
So the first thing I did, was to check if numpy is in my site-packages directory and indeed there is a folder named numpy-1.16.2-py3.7-win-amd64.egg. (Makes sense because the python interpreter can find numpy)
The next thing I did was to get some information about the sys.path variable created when running the script via the C-API.
#### sys.path content ####
C:\Work\build\product\python37.zip
C:\Work\build\product\DLLs
C:\Work\build\product\lib
C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO\2017\PROFESSIONAL\COMMON7\IDE\EXTENSIONS\TESTPLATFORM
C:\Users\rvq\AppData\Roaming\Python\Python37\site-packages
Examining the content of sys.path I noticed two things.
C:\Work\build\product\python37.zip has the correct path 'C:\Work\build\product\'. There was just no zip file. All my files and directory were unpacked. So I zipped the files to an archive named python37.zip and this resolved the import error.
C:\Users\rvq\AppData\Roaming\Python\Python37\site-packages is wrong it should be C:\Work\build\product\Lib\site-packages but I dont know how this wrong path is created.
The next thing I tried was to use Py_SetPath(L"C:/Work/build/product/Lib/site-packages") before calling Py_Initialize(). This led to
Fatal Python Error 'unable to load the file system encoding'
ModuleNotFoundError: No module named 'encodings'
I created a minimal c++ project with exact these two calls and started to debug Cpython.
int main()
{
Py_SetPath(L"C:/Work/build/product/Lib/site-packages");
Py_Initialize();
}
I tracked the call of Py_Initialize() down to the call of
static int
zipimport_zipimporter___init___impl(ZipImporter *self, PyObject *path)
inside of zipimport.c
The comment above this function states the following:
Create a new zipimporter instance. 'archivepath' must be a path-like
object to a zipfile, or to a specific path inside a zipfile. For
example, it can be '/tmp/myimport.zip', or
'/tmp/myimport.zip/mydirectory', if mydirectory is a valid directory
inside the archive. 'ZipImportError' is raised if 'archivepath'
doesn't point to a valid Zip archive. The 'archive' attribute of the
zipimporter object contains the name of the zipfile targeted.
So for me it seems that the C-API expects the path set with Py_SetPath to be a path to a zipfile. Is this expected behaviour or is it a bug?
If it is not a bug is there a way to changes this so that it can also detect directories?
PS: The ModuleNotFoundError did not occur for me when using Python 3.5.2+, which was the version I used in my project before. I also checked if I had set any PYTHONHOME or PYTHONPATH environment variables but I did not see one of them on my system.
Answer
This is probably a documentation failure more than anything else. We're in the middle of redesigning initialization though, so it's good timing to contribute this feedback.
The short answer is that you need to make sure Python can find the Lib/encodings directory, typically by putting the standard library in sys.path. Py_SetPath clears all inferred paths, so you need to specify all the places Python should look. (The rules for where Python looks automatically are complicated and vary by platform, which is something I'm keen to fix.)
Paths that don't exist are okay, and that's the zip file. You can choose to put the stdlib into a zip, and it will be found automatically if you name it the default path, but you can also leave it unzipped and reference the directory.
A full walk through on embedding is more than I'm prepared to type on my phone. Hopefully that's enough to get you going for now.
I had the problem and was tinkering with different solutions mentioned here. Since I was running my project from Visual Studio, apparently, I needed to set the environment path inside Visual Studio and not the system path.
Adding a simple PYTHONHOME=PATH\TO\PYTHON\DIR in the project solution\properties\environment solved the problem.
For me this happened when I updated Python 64 bit from 3.6.4 to 3.6.5. It threw some error like "unable to extract python.dll. Do you have permissions."
Pycharm also failed to load interpreter, even though I reloaded it in settings. Running python command gave same error, with and without administrator mode.
Reason
There was error in installation of Python, include folder in python installation directory C:\Users\USERNAME\AppData\Local\Programs\Python\Python36 was missing
Reinstalling Python also dint solve the issue.(Not removal and install)
Solution
Uninstall Python and Install of Python again.
Because running installer was just extracting same files excluding include folder
In my cases, for windows, if you have multiple python versions installed, if PYTHONPATH is pointing to one version the other ones didn't work. I found that if you just remove PYTHONPATH, they all work fine
For those working in Visual Studio simply add the include, Lib and libs directories to the Include Directories and Library Directories under
Projects Properties -> Configuration Properties > VC++ Directories :
For example I have Anaconda3 on my system and working with Visual Studio 2015 This is how the settings looks like (note the Include and Library directories) :
Edit:
As also pointed out by bossi setting PYTHONPATH in your user Environment Variables section seems necessary.
a sample input can be like this (in my case):
C:\Users\Master\Anaconda3\Lib;C:\Users\Master\Anaconda3\libs;C:\Users\Master\Anaconda3\Lib\site-packages;C:\Users\Master\Anaconda3\DLLs
is necessary it seems.
Also, you need to restart Visual Studio after you set up the PYTHONPATH in your user Environment Variables for the changes to take effect.
Also note that :
Make sure the PYTHONHOME environment variable is set to the Python
interpreter you want to use. The C++ projects in Visual Studio rely on
this variable to locate files such as python.h, which are used when
creating a Python extension.
So, for some reason the python dll fails to locate the encodings module. The python.exe executable apparently finds it because it has the expected relative path. Modifying the search path works.
The reason for all of this? Don't know but at least it works. I highly suspect a typo on my part somewhere, that's usually the reason for odd bugs it seems.

Using code generated by Py++ as a Python extension

I have a need to wrap an existing C++ library for use in Python. After reading through this answer on choosing an appropriate method to wrap C++ for use in Python, I decided to go with Py++.
I walked through the tutorial for Py++, using the tutorial files, and I got the expected output in generated.cpp, but I haven't figured out what to do in order to actually use the generated code as an extension I can import in Python. I'm sure I have to compile the code, now, but with what? Am I supposed to use bjam?
Py++ generates you syntax you use along with boost::python to generate python entry points in your app.
Assuming everything went well with Py++ you need to download the Boost framework, and add the boost include directory and the boost::python lib to your project then compile with the Py++ generated cpp.
You can use whatever build system you want for your project, but boost is built with bjam. You need to choose whether you want a static lib or a dynamic boost python lib then follow the instructions for building boost here.
If on windows, you need to change the extension on your built library from .dll to.pyd. And yes it needs to be a library project, this does not work with executables.
Then, place the pyd where the python on your machine can find it and go into python and execute import [Your-library-name] and hopefully everything will work.
One final note, the name given in generated.cpp in this macro:
BOOST_PYTHON_MODULE( -name- )
needs to be the exact name of your project, otherwise python will complain.
I just went through all this less than a month ago so I know about the confusion.
One thing I did to make my python extension very easy to use while building the library and testing, was to build boost::python and python myself in my build environment. That way the pyd ends up exactly where I want it and users do not need to install python in order to run with my extension. That may be overkill for what you are doing though.
Edit:
If you want your extension to be easily installed and compiled on a machine, check out python's setuptools. With just a few simple lines you can have python compile and install your package for you. One downside though is its not IDE compatible for those of us who like developing in visual studio.
The following answer was provided to me by Roman Yakovenko on the Python C++-sig mailing list; I'm posting it here, with minor edits, for the benefit of the Stack Overflow community.
I don't fully comprehend the answer yet, but I felt it points me in the right direction.
After you have generated the code, you have to compile it. For this purpose, you can use your favorite build system. I use bjam only to compile boost. After this, I prefer to use scons (on Windows and on Linux).
The following is an example of sconstruct file, which is used to compile one of the Py++ unittests (this is generated code too :-) ):
import sys
env = Environment()
if 'linux' not in sys.platform:
env['MSVS'] = {'VERSION': ''}
env['MSVS_VERSION'] = ''
Tool('msvc')(env)
t = env.SharedLibrary(
target=r'abstract_classes',
source=[r'/home/roman/language-binding/sources/pyplusplus_dev/unittests/temp/abstract_classes.cpp'],
LIBS=[r"boost_python"],
LIBPATH=[r"", r"/home/roman/include/libs"],
CPPPATH=[
r"/home/roman/boost_svn",
r"/usr/include/python2.6",
r"/home/roman/language-binding/sources/pyplusplus_dev/unittests/temp",
r"/home/roman/language-binding/sources/pyplusplus_dev/unittests/data",
r"/home/roman/boost_svn"
],
CCFLAGS=[ ],
SHLIBPREFIX='',
SHLIBSUFFIX='.so'
)
Since your code generator written in Python, you can continue where Py++ stops and generate your favorite "make" file. You can go even father. Py++ tests generate the code, compile, load the new module and test the functionality. All this is done in a single, stand alone process.
I wrote a small makefile with the following:
GNUmakefile:
PYTHON_INC=$(shell python-config --includes)
PYTHON_LIBS=$(shell python-config --libs)
BOOST_LIBS=-lboost_python
all:
g++ -W -Wall $(PYTHON_INC) $(PYTHON_LIBS) $(BOOST_LIBS) -fPIC -shared generated.cpp -o hw.so
and then loaded the created .so into ipython to play around with it:
In [1]: import hw
In [2]: a = hw.animal('zebra')
In [3]: a.name()
Out[3]: 'zebra'

building boost python examples using Visual Studio 2008

I'm using Boost Python library to create python extensions to my C++ code. I'd like to be able to invoke from python the 'greet' function from the C++ code shown below:
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
char const* greet()
{
return "hello, world";
}
BOOST_PYTHON_MODULE(hello_ext)
{
using namespace boost::python;
def("greet", greet);
}
And the python code :
import hello_ext
print hello_ext.greet()
I've managed to do this using the bjam (hello_ext.pyd is generated and it works nice), but now I'd like to build it using Visual Studio 2008. A hello.dll gets built (but neither hello_ext.dll nor any .pyd). After invoking my python code I get an error:
ImportError: No module named hello_ext.
After renaming the hello.dll to hello.pyd or hello_ext.pyd, I get another ImportError: Dll load failed
How can I build the correct .pyd file using VS 2008?
Firstly, make sure you only try to import the release version from Python; importing the debug version will fail because the runtime library versions don't match. I also change my project properties so that the release version outputs a .pyd file:
Properties >> Linker >> Output:
$(OutDir)\$(ProjectName).pyd
(I also create a post-build action to run unit tests from python)
Next, make sure you define the following in your stdafx.h file:
#define BOOST_PYTHON_STATIC_LIB
Lastly, if you have more than one python version installed, make sure that you're importing the right version of python.h (in Tools >> Options >> Projects and Solutions >> VC++ Directories >> Include Files).
The error ImportError: Dll load failed usually means that your .pyd module depends on other DLLs that couldn't be found - often msvc*.dll. You may want to try opening the .pyd file in Notepad and searching for ".dll". Then check if all of the DLL dependencies exist in your directory or PATH.
Or use Dependency Walker which will find missing dependencies for you
Even though this is a question issued several years ago(still not easy to find solution), but I meet the same problem today, and after hours searching, finally I found a feasible solution.
The reason is just as simple as which is noticed by #AndiDog, the .pyd file you build depends on some other .dll;
In my case, It's boost_python-vc120-mt-1_58.dll under the folder [C++ boost folder]/stage/lib/
So, what I do is to copy this file, and paste it under the .pyd file folder, and then my python can properly import the project I build .
maybe there are some other solutions, that is build your project not depend on dynamic library, use static library instead. some of the source said to define BOOST_PYTHON_STATIC_LIB in VS Preprocessor, then your project will not depend on dynamic library(I am a new C++er), but be sure you have build libboost_python-vcXXX-mt-1_58.dll in boost.
to define Preprocessor, the route is:C/C++->Preprocessor->Preprocessor Definitions->edit BOOST_PYTHON_STATIC_LIB
Please make sure you have flag -lpython26 (if you are using python2.6) and filename should be hello_ext.pyd in your case.