注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Code@Pig Home

喜欢背着一袋Code傻笑的Pig .. 忧美.欢笑.记忆.忘却 .之. 角落

 
 
 

日志

 
 

Cython 第0章 - distutils Quick Guide  

2016-04-18 10:27:49|  分类: lang_python |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
Cython 第0章 - distutils Quick Guide - kasicass - Code@Pig Home
(勿在浮沙筑高台)

本章不属于 Cython Book。但 Cython 和 distutils 关系比较紧密,独立一章来讲讲 distutils。
内容来自 Python Doc 中的《Distributing Python Modules》。

来看 distutils 一个最简单的例子
# foo.py
MYVALUE = 10

def printme():
    print 'hello'

# setup.py
from distutils.core import setup

setup(
  name='foo',
  version='1.0',
  py_modules=['foo']
)

> python setup.py sdist
可以得到一个 dist/foo-1.0.zip,发布的包。sdist 指 source dist

> python setup.py bdist_wininst
可以得到一个 dist/foo-1.0.win32.exe 的安装程序。bdist 是 binary dist

>python setup.py bdist --help-formats
List of available distribution formats:
  --formats=rpm      RPM distribution
  --formats=gztar    gzip'ed tar file
  --formats=bztar    bzip2'ed tar file
  ...
可看到所支持的所有 bdist 格式。

如何打包多个目录:
/setup.py
/foomodule/__init__.py
/foomodule/foo.py
/barmodule/__init__.py
/barmodule/bar.py

# setup.py
from distutils.core import setup

setup(
  name='multimodule',
  version='1.0',
  description='multi-module test',
  author='your_name',
  author_email='your_email',
  url='...',
  packages=['foomodule', 'barmodule']
)

> python setup.py install
> python
>>> from foomodule import foo
>>> foo.foo()
foo

看看 Python27 的第三方目录:
C:\Python27\Lib\site-packages
可以看到 [foomodule] 和 [barmodule]。

看看安装了哪些 package
> python -m pip list

将 multimodule 这个 package 删掉
> python -m pip uninstall multimodule

Win32 下让 distutils 可以编译 C 代码
Python 2.7 对应的是 VC++ 2008 的 C/C++ Compiler。
如果没装这个,下面 build_ext 的时候,会碰到 error: Unable to find vcvarsall.bat

然后去增加一个环境变量 VS90COMNTOOLS,比如我的 VC++ for Python 目录如下:
C:\Users\kasicass\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\

修改 C:\Python27\Lib\distutils\msvc9compiler.py 中 def find_vcvarsall() 函数,其中
productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
改为
productdir = toolsdir
(=_=! 在 win32 下编译 python package,好坑~)

编译一个 extension module
extension module 就是带有 C/C++ 代码的 module。
直接上代码:

# noddy.cpp
#include <Python.h>

typedef struct {
  PyObject_HEAD
} noddy_NoddyObject;

static PyTypeObject noddy_NoddyType = {
  PyObject_HEAD_INIT(NULL)
  0,                         /*ob_size*/
  "noddy.Noddy",             /*tp_name*/
  sizeof(noddy_NoddyObject), /*tp_basicsize*/
  0,                         /*tp_itemsize*/
  0,                         /*tp_dealloc*/
  0,                         /*tp_print*/
  0,                         /*tp_getattr*/
  0,                         /*tp_setattr*/
  0,                         /*tp_compare*/
  0,                         /*tp_repr*/
  0,                         /*tp_as_number*/
  0,                         /*tp_as_sequence*/
  0,                         /*tp_as_mapping*/
  0,                         /*tp_hash */
  0,                         /*tp_call*/
  0,                         /*tp_str*/
  0,                         /*tp_getattro*/
  0,                         /*tp_setattro*/
  0,                         /*tp_as_buffer*/
  Py_TPFLAGS_DEFAULT,        /*tp_flags*/
  "Noddy objects",           /* tp_doc */
};

static PyMethodDef noddy_methods[] = {
  {NULL}  /* Sentinel */
};

PyMODINIT_FUNC
initnoddy(void)
{
  PyObject *m;
  noddy_NoddyType.tp_new = PyType_GenericNew;

  if (PyType_Ready(&noddy_NoddyType) < 0)
    return;

  Py_INCREF(&noddy_NoddyType);
  m = Py_InitModule3("noddy", noddy_methods, "example module");
  PyModule_AddObject(m, "Noddy", (PyObject*)&noddy_NoddyType);
}

# setup.py
from distutils.core import setup, Extension

setup(
  name="noddy",
  version="1.0",
  author="your_name",
  author_email="your_email",
  ext_modules=[Extension(
    "noddy",
    ["noddy.cpp"],
  )]
)

看看如何编译:
> python setup.py build_ext -i

build_ext 加上 -i 参数,会在当前目录生成 .pyd,方便 import 和 测试。
> python
>>> import noddy
>>> obj = noddy.Noddy()
>>> obj
<noddy.Noddy object at 0x026684E0>

win32 下 extension module 的坑(请认真阅读)
如果代码中用到了 PyType_Type,而且你的代码后缀又是 .c 则会碰到:
noddy.c
noddy.c(8) : error C2099: initializer is not a constant

对应代码
static PyTypeObject noddy_NoddyType = {
  PyObject_HEAD_INIT(&PyType_Type)  // <-- 说这里不允许初始化

我错误的给 cl.exe 加上了 /DPy_NO_ENABLE_SHARED,改成 static-link 然后再次编译
  ext_modules=[Extension(
    "noddy",
    ["noddy.c"],
    extra_compile_args = ['/DPy_NO_ENABLE_SHARED']
  )]

然后就找不到 _PyType_Type 这个 symbol 了:
noddy.obj : error LNK2001: unresolved external symbol _PyType_Type
noddy.obj : error LNK2019: unresolved external symbol _PyModule_AddObject referenced in function _initnoddy

最后发现还是 .c 后缀的问题。VC++ 对 C 的有限制:

如何绕过 VC++ 这个问题?=_= 把 .c 改为 .cpp 即可。bug重现见下面的代码:
> python setup.py build_ext      # error C2099
> python setup_cpp.py build_ext  # ok
(ps. gcc下没有这个问题)

结论:将 C/C++ 内容注册到 Python 的相关代码,文件要用 .cpp 为后缀!!!才能保证全平台通用。
  评论这张
 
阅读(323)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017