如何使用setuptools创建包含多个模块的Python包?

3 投票
1 回答
4073 浏览
提问于 2025-04-18 15:59

我想组织一个包含多个模块的分发包。最终会有一个C语言扩展模块,同时还有几个纯Python模块。我不太确定顶层应该仅仅被视为一个命名空间。

首先,我想创建一个名为monty.spam的模块。这个模块会是一个C语言扩展。我从扩展Python的文档中提取了这个内容。

monty/spam/spammodule.c

#include <Python.h>

static PyObject * spam_system (PyObject * self, PyObject * args)
  {
  const char * command;
  int          sts;

  if (!PyArg_ParseTuple (args, "s", &command))
    return NULL;
  sts = system (command);
  return Py_BuildValue ("i", sts);
  }

static 
PyMethodDef SpamMethods [] = {
             { "system", spam_system, METH_VARARGS, "Execute a shell command" },
             { NULL, NULL, 0, NULL }    
             };

PyMODINIT_FUNC
initspam (void)
  {
  (void) Py_InitModule ("spam", SpamMethods);
  }

我在同一个目录下创建了一个setup工具的setup.py,运行了“python setup.py develop”,结果模块运行得很好。

monty/spam/setup.py

from setuptools import setup, Extension

module = Extension ('spam', sources = [ 'spammodule.c' ])

setup (
      name = 'MontyP',
      version = '0.2', 
      description = 'pure spam',
      ext_modules = [ module ]
      )

在monty目录下:

python -c 'import monty.spam'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: No module named monty.spam

现在我想创建一个分发包,让spam“存在于”monty中。我尝试了多种不同的setup.py配置在monty目录下。(这两个目录都有一个空的__init__.py文件)

我最近尝试的monty/setup.py是这样的:(是的,这个版本的find_packages()没有用处。)

from setuptools import setup, Extension, find_packages

module = Extension ('spam', sources = [ './spam/spammodule.c' ])

print find_packages ()
setup (
      name               = 'MontyP',
      version            = '0.2', 
      namespace_packages = [ 'monty' ],
      packages           = find_packages (),
      description        = 'pure spam',
      ext_modules        = [ module ]
      )

结果不太理想!

python setup.py develop
['spam']
error in MontyP setup command: Distribution contains no modules or packages for namespace package 'monty'

1 个回答

1

你只需要做两到三处修改就能让它正常工作。

首先,setup.py 文件应该放在和 monty/ 同一级别,而不是放在 monty/spam 里面:

在这里输入图片描述

setup.py 文件总是放在项目的根目录。

另外,在 monty 文件夹里创建一个 __init__.py 文件,但spam 文件夹里不要创建 __init__.py

接下来,在 setup.py 文件中,把这一行 "module = Extension ('spam', sources = [ 'spammodule.c' ])" 中的 spam 替换成 monty.spam,把 spammodule.c 替换成 monty/spam/spammodule.c

module = Extension ('monty.spam', sources = [ 'monty/spam/spammodule.c' ])

这样做是必要的,因为外部模块需要用完整的名字来声明(也就是用 monty.spam 而不是 spam)。而且,由于现在 setup.py 在项目的根目录,所以它需要一个相对但完整的路径来找到 C 源文件。

就这样,你的模块现在应该可以正常工作了。

撰写回答