setup.py not installing swig extension module

Viewed 1250

I'm struggling to figure out how to copy the wrapper generated by swig at the same level than the swig shared library. Consider this tree structure:

│   .gitignore
│   setup.py
│
├───hello
├───src
│       hello.c
│       hello.h
│       hello.i
│
└───test
        test_hello.py

and this setup.py:

import os
import sys

from setuptools import setup, find_packages, Extension
from setuptools.command.build_py import build_py as _build_py


class build_py(_build_py):
    def run(self):
        self.run_command("build_ext")
        return super().run()


setup(
    name='hello_world',
    version='0.1',
    cmdclass={'build_py': build_py},
    packages=["hello"],
    ext_modules=[
        Extension(
            'hello._hello',
            [
                'src/hello.i',
                'src/hello.c'
            ],
            include_dirs=[
                "src",
            ],
            depends=[
                'src/hello.h'
            ],
        )
    ],
    py_modules=[
        "hello"
    ],
)

When I do pip install . I'll get this content on site-packages:

>tree /f d:\virtual_envs\py364_32\Lib\site-packages\hello                            
D:\VIRTUAL_ENVS\PY364_32\LIB\SITE-PACKAGES\HELLO                                                                 
    _hello.cp36-win32.pyd                                                                                        


>tree /f d:\virtual_envs\py364_32\Lib\site-packages\hello_world-0.1.dist-info        
D:\VIRTUAL_ENVS\PY364_32\LIB\SITE-PACKAGES\HELLO_WORLD-0.1.DIST-INFO                                             
    INSTALLER                                                                                                    
    METADATA                                                                                                     
    RECORD                                                                                                       
    top_level.txt                                                                                                
    WHEEL  

As you can see hello.py (the file generated by swig) hasn't been copied in site-packages.

Thing is, I've already tried many answers from the below similars posts:

Unfortunately, the question still remains unsolved.

QUESTION: How can I fix my current setup.py so the swig wrapper will be copied at the same level than the .pyd file?

1 Answers

setuptools cannot do this the way you want: it will look for py_modules only where setup.py is located. The easiest way IMHO is keeping the SWIG modules where you want them in the namespace/directory structure: rename src to hello, and add hello/__init__.py (may be empty or simply include everything from hello.hello), leaving you with this tree:

$ tree .
.
├── hello
│   ├── __init__.py
│   ├── _hello.cpython-37m-darwin.so
│   ├── hello.c
│   ├── hello.h
│   ├── hello.i
│   ├── hello.py
│   └── hello_wrap.c
└── setup.py

Remove py_modules from setup.py. The "hello" in the package list will make setuptools pick up the whole package, and include __init__.py and the generated hello.py:

import os
import sys

from setuptools import setup, find_packages, Extension
from setuptools.command.build_py import build_py as _build_py


class build_py(_build_py):
    def run(self):
        self.run_command("build_ext")
        return super().run()


setup(
    name='hello_world',
    version='0.1',
    cmdclass={'build_py': build_py},
    packages = ["hello"],
    ext_modules=[
        Extension(
            'hello._hello',
            [
                'hello/hello.i',
                'hello/hello.c'
            ],
            include_dirs=[
                "hello",
            ],
            depends=[
                'hello/hello.h'
            ],
        )
    ],
)

This way, also .egg-linking the package works (python setup.py develop), so you can link the package under development into a venv or so. This is also the reason for the way setuptools (and distutils) works: the dev sandbox should be structured in a way that allows running the code directly from it, without moving modules around.

The SWIG-generated hello.py and the generated extension _hello will then live under hello:

>>> from hello import hello, _hello
>>> print(hello)
<module 'hello.hello' from '~/so56562132/hello/hello.py'>
>>> print(_hello)
<module 'hello._hello' from '~/so56562132/hello/_hello.cpython-37m-darwin.so'>

(as you can see from the extension filename, I am on a Mac right now, but this works exactly the same under Windows)

Also, beyond packaging, there's more useful information about SWIG and Python namespaces and packages in the SWIG manual: http://swig.org/Doc4.0/Python.html#Python_nn72

Related