如何使用同名的setuptools包和ext_modules?

How to use setuptools packages and ext_modules with the same name?

我的 Python C 扩展项目得到以下文件结构:

.
├── setup.py
├── source
    ├── cppimplementation
    │   └── fastfile.cpp
    └── fastfilepackage
        ├── __init__.py
        └── version.py

我使用以下 setup.py 文件:

from setuptools import setup, Extension

setup(
        name= 'fastfilepackage',
        version= '0.1.1',
        package_dir = {
            '': 'source',
        },

        packages = [
            'fastfilepackage',
        ],

        ext_modules= [
            Extension(
                'fastfilepackage',
                [
                    'source/cppimplementation/fastfile.cpp',
                ]
            )
        ],
    )

我安装它们:

$ pip3 --version
pip 19.1.1 (python 3.6)

$ python3 --version
Python 3.6.7

$ pip3 list
Package                Version      
---------------------- -------------
wheel                  0.33.1        
setuptools             40.8.0        
...

fastfilepackage$ pip3 install .

问题是当我安装它时,我的 Python C 扩展模块被 fastfilepackage/version.pyfastfilepackage/__init__.py 覆盖,即安装后,我得到以下信息:

import fastfilepackage
print( dir( fastfilepackage ) )
# prints ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', 
#         '__name__', '__package__', '__path__', '__spec__', '__version__',
#         'version']

source/cppimplementation/fastfile.cpp 没有导出 FastFile class,但它有 fastfilepackage/version.pyfastfilepackage/__init__.py 文件。

这是安装的文件结构:

.
└── dist-packages
    ├── fastfilepackage
    │   ├── __init__.py
    │   ├── __pycache__
    │   │   ├── __init__.cpython-36.pyc
    │   │   └── version.cpython-36.pyc
    │   └── version.py
    ├── fastfilepackage-0.1.1.dist-info
    │   ├── INSTALLER
    │   ├── LICENSE.txt
    │   ├── METADATA
    │   ├── RECORD
    │   ├── top_level.txt
    │   └── WHEEL
    └── fastfilepackage.cpython-36m-x86_64-linux-gnu.so

但是,如果我从安装文件中删除行 package_dir = { '': 'source', },packages = [ 'fastfilepackage', ],,那么,我的 Python C 扩展模块已正确安装:

import fastfilepackage
print( dir( fastfilepackage ) )
# prints ['FastFile', '__doc__', '__file__', '__loader__', '__name__', 
#         '__package__', '__spec__']

即,它有 source/cppimplementation/fastfile.cpp 导出的 FastFile class,但没有 fastfilepackage/version.pyfastfilepackage/__init__.py 文件。

这是安装的文件结构:

.
└── dist-packages
    ├── fastfilepackage-0.1.1.dist-info
    │   ├── INSTALLER
    │   ├── LICENSE.txt
    │   ├── METADATA
    │   ├── RECORD
    │   ├── top_level.txt
    │   └── WHEEL
    └── fastfilepackage.cpython-36m-x86_64-linux-gnu.so

我怎样才能让 ext_modulespackages 在我的 setup.py 下使用相同的包名称而不被一个覆盖另一个?

你不能。第一个导入的获胜。您不能让 scripts/modules/packages/extensions 具有相同的名称 - 一个会覆盖所有其他名称。

但是你可以把一个放在另一个里面。将你的扩展命名为 fastfilepackage.fastfilepackage 然后你可以 import fastfilepackage 导入 Python 包和 import fastfilepackage.fastfilepackage 导入扩展;或 from fastfilepackage import fastfilepackage.

作为最终解决方案,我完全删除了所有 Python *.py 代码,因为它们导致 C 扩展代码变慢了 30%。现在我的setup.py变成了这样:

from setuptools import setup, Extension

setup(
        name = 'fastfilepackage',
        version = '0.1.1',

        ext_modules = [
            Extension(
                name = 'fastfilepackage',
                sources = [
                    'source/fastfile.cpp',
                ],
                include_dirs = ['source'],
            )
        ],
    )

文件结构:

.
├── setup.py
├── MANIFEST.in
├── README.md
├── LICENSE.txt
└── source
    ├── fastfile.cpp
    └── version.h

MANIFEST.in

include README.md
include LICENSE.txt

recursive-include source *.h

这是已安装的文件结构:(无 *.py 文件在任何地方 = 100% 性能)

.
└── dist-packages
    ├── fastfilepackage-0.1.1.dist-info
    │   ├── INSTALLER
    │   ├── LICENSE.txt
    │   ├── METADATA
    │   ├── RECORD
    │   ├── top_level.txt
    │   └── WHEEL
    └── fastfilepackage.cpython-36m-x86_64-linux-gnu.so

我只是直接用 C 扩展模块属性替换了 version.py

// https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue
const char* __version__ = "0.1.1";
PyObject_SetAttrString( thismodule, "__version__", Py_BuildValue( "s", __version__ ) );

参考文献:

  1. Define a global in a Python module from a C API
  2. 原始提交:Deprecated the Python implementation *.py