【问题标题】:How to properly package a project with Setuptools in Python?如何在 Python 中使用 Setuptools 正确打包项目?
【发布时间】:2019-11-22 14:06:16
【问题描述】:

这是我的目录树:

prediction_model
├─ prediction_model
|  ├─ __init__.py
|  ├─ data
|  |  ├─ SAAA.csv
|  |  └─ VDFF.csv
|  ├─ models.py
|  ├─ preprocess.py
|  ├─ README.md
|  └─ tests
└─ setup.py

这是我的“setup.py”:

from setuptools import find_packages, setup

setup(
    name='prediction_model',
    version='0.7',
    url='https://project.org/',
    author='JL',
    author_email='jl@project.org',
    packages=find_packages(),
    scripts=['models.py', 'preprocess.py']
)

这是我的“__init__.py”:

from prediction_model import models
from prediction_model import preprocess

‘models.py’有一个函数main,‘preprocess.py’有一个我想使用的函数run

我使用以下方式安装项目:

python -m pip install --user .

然后我在 Python 解释器中运行以下代码,但它引发了异常 AttributeError

>>> import prediction_model
>>> prediction_model.src.main()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'prediction_model' has no attribute 'src'
>>> prediction_model.src.run()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'prediction_model' has no attribute 'src'
>>> import prediction_model
>>> prediction_model.main()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'prediction_model' has no attribute 'main'
>>> prediction_model.run()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'prediction_model' has no attribute 'run'

我做错了什么?

环境:Python 3.7、MacOS。

【问题讨论】:

  • 安装后,当您在客户端代码中执行 import prediction_model 时,它会隐式运行 __init__.py 文件。因为你在其中做from prediction_model import preprocess, models,所以preprocessmodels 模块将在你的命名空间中。然后,不要在您的客户端代码中执行prediction_model.src.main(),这会引发AttributeError,因为您的命名空间中没有src 对象,您应该只执行prediction_model.preprocess.run()prediction_model.models.main()
  • 如果你想在你的客户端代码中使用prediction_model.run()prediction_model.main(),你应该在你的__init__.py文件中使用from prediction_model.preprocess import run; from prediction_model.models import main
  • 另外,你的scripts 参数应该是['prediction_model/models.py', 'prediction_model/preprocess.py'] 而不是['models.py', 'preprocess.py'](因为相对路径是根据setup.py 的目录解析的)。此外,models.py 和 preprocess.py 文件的顶部需要一个 shebang 行,否则安装将失败:#!/usr/bin/env python。但是由于它们是 Python 脚本并且是 prediction_model 包的一部分,因此您应该使用 entry_points 参数而不是 scripts 参数,并且您不需要这个 shebang 行。跨度>

标签: python package setuptools setup.py


【解决方案1】:

设置

最好的做法是使用 src 布局(参见https://blog.ionelmc.ro/2014/05/25/python-packaging/),所以我建议你像这样组织你的目录(因为你不应该打包'tests'目录和' README.rst' 文件用于构建分发,它们位于 'src' 目录之外):

project
├─ src
|  └─ package
|     ├─ __init__.py
|     ├─ __main__.py
|     ├─ data.tsv
|     └─ module.py
├─ tests
|  └─ test_module.py
├─ MANIFEST.in
├─ README.rst
└─ setup.py

具有以下“setup.py”文件内容:

import setuptools

setuptools.setup(
    name="project name",
    version="project version",
    url="project URI",
    author="your name",
    author_email="your email",
    package_dir={"": "src"},
    packages=setuptools.find_namespace_packages(where="src"),
    include_package_data=True
)

以及以下“MANIFEST.in”文件内容:

graft src
graft tests
global-exclude *.py[cod]

备注。——如果你不想在你的 'src' 目录中有一个顶级的 'package' 目录,同时保留 'src' 目录(就像你的帖子编辑之前一样): p>

project
├─ src
|  ├─ __init__.py
|  ├─ __main__.py
|  ├─ data.tsv
|  └─ module.py
├─ tests
|  └─ test_module.py
├─ MANIFEST.in
├─ README.rst
└─ setup.py

然后将"package" 映射到package_dir 参数中的"src",并在setup.py 文件中setuptools.setup 函数的packages 参数中显式列出"package"(参见https://docs.python.org/3/distutils/examples.html#pure-python-distribution-by-package):

import setuptools

setuptools.setup(
    name="project name",
    version="project version",
    url="project URI",
    author="your name",
    author_email="your email",
    package_dir={"package": "src"},
    packages=["package"],
    include_package_data=True
)

现在您可以使用以下命令将项目打包到 source 发行版中:

python setup.py sdist

或使用此命令的构建发行版(这是setuptools 的扩展命令,要求在您的Python 环境中安装wheel - 所以之前运行pip install wheel):

python setup.py bdist_wheel

或两者都用这个命令:

python setup.py sdist bdist_wheel

在新的dist 目录中创建以下文件:

  • “{项目名称}-{项目版本}.tar.gz”用于源代码分发;
  • '{project name}-{project version}-{compatibility tags}.whl' 用于构建发行版。

安装

现在您可以使用以下命令从源代码分发版安装项目:

pip install dist/{project name}-{project version}.tar.gz

或使用此命令从构建的发行版中:

pip install dist/{project name}-{project version}-{compatibility tags}.whl

或使用此命令从项目树中(无需为此打包项目):

pip install .

运行

最终你可以像这样导入你的项目:

import package.module

package.module.function()

【讨论】:

  • 这不是真正的 src 布局。他们只是将他们的顶级包命名为src。而且我相信他们希望它可以以项目prediction_model 的名义导入,这当然行不通。
  • 编辑得很好。我删除了我的反对票。为什么是find_namespace_packages 而不是find_packages?是不是有点误导?
  • 谢谢。 find_namespace_packagesfind_packages 的现代版本(不应再使用),因为它支持 Python 3.3 中引入的隐式命名空间包(参见 setuptools.readthedocs.io/en/latest/…)。
  • 在我看来,现在有太多不相关的细节(MANIFEST.ininclude_package_data)、车轮等)。为什么不只关注原始问题:顶级包的名称错误。指向通用包装指南的完善参考的链接对于额外的细节就足够了。尽管一切似乎都是好建议。
  • @sinoroc 实际上没有MANIFEST.ininclude_package_data=True.py 文件(如提问者的.csv 文件)将包含在打包/安装的项目中,所以它们是必要的(参见setuptools.readthedocs.io/en/latest/…)。另一种方法是使用 package_data 参数,但它仅用于 built 发行版,因此您仍然必须将 MANIFEST.in 用于 source 发行版;因为它重复信息所以不鼓励(参见blog.ionelmc.ro/presentations/packaging/#slide:15)。
【解决方案2】:

通常的项目结构:(长话短说,您想要一个带有您项目名称的目录,以便 setup.py 获取它,这是通常的方式,看看几个 repos,例如https://github.com/boto/boto3)。

-- setup.py
-- README.md
--- prediction_model
    |-- __init__.py
    |-- data
    |   |-- VDFF.csv
    |   +-- SAAA.csv
    |-- models.py
    |-- preprocess.py
    |-- tests
    +-- _data.csv

然后你想做这样的事情:

virtualenv myvirtualenv 
source myvirtualenv/bin/activate
python setup.py install

您的设置看起来不错,现在 prediction_model 是一个您也可以更改的包:

from . import preprocess
from . import models

from prediction_model import preprocess
from prediction_model import models

正常设置.py

from setuptools import setup, find_packages

setup(
    name='cost_estimation',
    version='0.0.1',
    packages=find_packages(),
    url='http://github.com/<WHATEVER/WHATEVER>',
    install_requires=[
        'numpy',
    ],
    tests_require=[
         'nosetests',
    ],
    test_suite='nose.collector',
    license='',
    dependency_links=[
        'http://YOURPRIVATEPYPISERVER/packages'
    ],

    keywords='models, prediction',
    author='your name',
    description='Model prediction cool stuff'
)

【讨论】:

  • python-packaging-tutorial.readthedocs.io/en/latest/… pip install . 相当于python -m pip install --user .,开发模式。我出于某种原因需要使用 root 权限进行安装。
  • plus..ImportError: No module named setuptools 总是返回,尽管我可以在加载 python 时导入这个模块。我就是不明白。
  • 使用同名,不会混淆打包工具吗?反正这个setuptools没用,好像通过find_packages()找不到包
  • 从我的角度来看,这个答案中有一些多余的(并且可能是误导性的细节),但基本上,是的,这看起来像是一个正确的答案。简而言之,将您的src 目录重命名为您希望能够导入的名称,即prediction_model。请注意,它不必与项目名称相同(setup.py 中的那个)。
  • 但感谢您引导我朝着正确的方向前进@sinoroc 和@E.Serra。尝试一下,至少有一个开始。现在调用脚本。 prediction_model.preprocess.run(), prediction_model.models.main() 像这样。还有许多其他错误。
猜你喜欢
  • 2016-12-25
  • 2012-04-01
  • 2018-08-03
  • 2017-07-12
  • 1970-01-01
  • 2011-04-04
  • 2021-01-20
  • 2021-07-07
  • 2016-05-12
相关资源
最近更新 更多