pip install . 只会创建 dist-info 而不是包。

106

我正在尝试创建一个python包,我希望能够使用pip install .本地安装。该包的名称在pip freeze中列出,但是import <package>导致错误No module named <package>。同时,site-packages文件夹仅包含dist-info文件夹。find_packages()可以找到包。我错过了什么吗?

import io
import os
import sys
from shutil import rmtree

from setuptools import find_packages, setup, Command

# Package meta-data.
NAME = '<package>'
DESCRIPTION = 'description'
URL = ''
EMAIL = 'email'
AUTHOR = 'name'

# What packages are required for this module to be executed?
REQUIRED = [
    # 'requests', 'maya', 'records',
]

# The rest you shouldn't have to touch too much :)
# ------------------------------------------------
# Except, perhaps the License and Trove Classifiers!
# If you do change the License, remember to change the Trove Classifier for that!

here = os.path.abspath(os.path.dirname(__file__))



# Where the magic happens:
setup(
    name=NAME,
    #version=about['__version__'],
    description=DESCRIPTION,
    # long_description=long_description,
    author=AUTHOR,
    author_email=EMAIL,
    url=URL,
    packages=find_packages(),
    # If your package is a single module, use this instead of 'packages':
    # py_modules=['mypackage'],

    # entry_points={
    #     'console_scripts': ['mycli=mymodule:cli'],
    # },
    install_requires=REQUIRED,
    include_package_data=True,
    license='MIT',
    classifiers=[
        # Trove classifiers
        # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
        'License :: OSI Approved :: MIT License',
        'Programming Language :: Python',
        'Programming Language :: Python :: 2.6',
        'Programming Language :: Python :: 2.7',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.3',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: Implementation :: CPython',
        'Programming Language :: Python :: Implementation :: PyPy'
    ],

)

7
运行命令 pip uninstall -y pkgname && pip install -v .,并检查构建日志以确定源文件是否已被处理(寻找包含 copying file <filename>adding <filename> 的行)。如果这些文件已被添加,请运行 pip show -f pkgname 命令,并将两个命令的输出添加到问题中。 - hoefling
1
非常感谢。这让我意识到,我给 setup.py 分配的包名称与目录名称不同。 - André Betz
我曾经遇到同样的问题,直到我意识到这是一个愚蠢的错误:我在项目还在复制到新位置的时候就开始了构建过程。这样做的结果是,它并没有包含任何Python代码。所有尝试使用不同设置安装轮子都注定失败,因为实际上轮子并没有包含任何可用的代码文件可以安装。 - Cerno
hoefling,您能否根据您的评论输入更完整的答案?我不知道如何检查构建日志,也不确定您希望我们向pip show添加什么。 - Jake Stevens-Haas
5个回答

147

由于这个问题变得非常流行,所以以下是在安装后遗失文件时需要进行的诊断步骤。假设有一个带有以下结构的示例项目:

root
├── spam
│   ├── __init__.py
│   ├── data.txt
│   ├── eggs.py
│   └── fizz
│       ├── __init__.py
│       └── buzz.py
├── bacon.py
└── setup.py

现在我运行 pip install . 命令,并检查包是否已安装:

$ pip list
Package    Version
---------- -------
mypkg      0.1    
pip        19.0.1 
setuptools 40.6.3 
wheel      0.32.3 

但在已安装软件包的文件列表中,既看不到 spam 也看不到 spam/eggs.pybacon.pyspam/fizz/buzz.py

$ pip show -f mypkg
Name: mypkg
Version: 0.1
...
Files:
  mypkg-0.1.dist-info/DESCRIPTION.rst
  mypkg-0.1.dist-info/INSTALLER
  mypkg-0.1.dist-info/METADATA
  mypkg-0.1.dist-info/RECORD
  mypkg-0.1.dist-info/WHEEL
  mypkg-0.1.dist-info/metadata.json
  mypkg-0.1.dist-info/top_level.txt

现在该怎么办?

通过检查轮构建日志进行诊断

除非被告知不这样做,pip总会尝试构建轮文件并从中安装您的软件包。如果在详细模式下重新安装软件包,我们可以检查轮构建过程的日志。第一步是卸载软件包:

$ pip uninstall -y mypkg
...

然后再次安装它,但这次需要加上一个额外的参数:

$ pip install . -vvv
...

现在,如果我检查日志:

$ pip install . -vvv | grep 'adding'
  adding 'mypkg-0.1.dist-info/METADATA'
  adding 'mypkg-0.1.dist-info/WHEEL'
  adding 'mypkg-0.1.dist-info/top_level.txt'
  adding 'mypkg-0.1.dist-info/RECORD'

我注意到在任何地方都没有提到spam目录或者bacon.py文件,这意味着它们并没有被包含在wheel文件中,因此也没有被pip安装。最常见的错误来源包括:

缺失的包:检查packages参数

请确认您已经向setup函数传递了packages参数,并且检查您是否已经列出了所有应该被安装的包。如果仅仅只是指定了父包,那么子包不会被自动收集!例如,在安装脚本中:

from setuptools import setup

setup(
    name='mypkg',
    version='0.1',
    packages=['spam']
)

spam将被安装,但不会安装spam.fizz,因为它是一个包本身,并且必须显式地提及。修复方法:

from setuptools import setup

setup(
    name='mypkg',
    version='0.1',
    packages=['spam', 'spam.fizz']
)

如果你有很多的包,可以使用 setuptools.find_packages 自动化处理过程:

from setuptools import find_packages, setup

setup(
    name='mypkg',
    version='0.1',
    packages=find_packages()  # will return a list ['spam', 'spam.fizz']
)

如果您缺少模块:

缺失的模块: 检查py_modules参数

在上面的示例中,由于它不属于任何软件包,因此在安装后我将缺少bacon.py。我必须在单独的参数py_modules中提供其模块名称:

from setuptools import find_packages, setup

setup(
    name='mypkg',
    version='0.1',
    packages=find_packages(),
    py_modules=['bacon']
)

缺少的数据文件:请检查package_data参数

我现在已经准备好了所有源代码文件,但是data.txt文件仍未安装。位于包目录下的数据文件应通过package_data参数添加。修复上述安装脚本:

from setuptools import find_packages, setup

setup(
    name='mypkg',
    version='0.1',
    packages=find_packages(),
    package_data={'spam': ['data.txt']},
    py_modules=['bacon']
)

不要尝试使用 data_files 参数,将数据文件放在一个包下并配置 package_data

修复设置脚本后,请确认安装后包文件的位置是否正确。

如果现在重新安装该包,您会注意到所有文件都已添加到 wheel 中:

$ pip install . -vvv | grep 'adding'
  adding 'bacon.py'
  adding 'spam/__init__.py'
  adding 'spam/data.txt'
  adding 'spam/eggs.py'
  adding 'spam/fizz/__init__.py'
  adding 'spam/fizz/buzz.py'
  adding 'mypkg-0.1.dist-info/METADATA'
  adding 'mypkg-0.1.dist-info/WHEEL'
  adding 'mypkg-0.1.dist-info/top_level.txt'
  adding 'mypkg-0.1.dist-info/RECORD'

它们也将在属于 mypkg 的文件列表中可见:

$ pip show -f mypkg
Name: mypkg
Version: 0.1
...
Files:
  __pycache__/bacon.cpython-36.pyc
  bacon.py
  mypkg-0.1.dist-info/INSTALLER
  mypkg-0.1.dist-info/METADATA
  mypkg-0.1.dist-info/RECORD
  mypkg-0.1.dist-info/WHEEL
  mypkg-0.1.dist-info/top_level.txt
  spam/__init__.py
  spam/__pycache__/__init__.cpython-36.pyc
  spam/__pycache__/eggs.cpython-36.pyc
  spam/data.txt
  spam/eggs.py
  spam/fizz/__init__.py
  spam/fizz/__pycache__/__init__.cpython-36.pyc
  spam/fizz/__pycache__/buzz.cpython-36.pyc
  spam/fizz/buzz.py

1
我应该补充说明一下,我很好奇为什么import mypkg会失败。 - user1717828
3
你是想导入分发名称(在安装脚本中放置在“name”参数下的内容)还是其中一个包名称(在“packages”参数下放置的内容)?你无法导入分发名称。总的来说,这与此问题无关,我建议您提出另一个问题。 - hoefling
5
非常好的回答!谢谢。 - f10w
5
另一个常见的错误来源:每个包都必须有一个 __init__.py 文件,才能让 setuptools 找到它。如果你习惯于使用开发安装 (-e),则很容易忘记这一点,因为开发安装并不需要该文件。 - Tristan Brown
1
@hoefling 非常感谢您详细的回答。您教会了我如何钓鱼 ;) - eidal
显示剩余12条评论

2
请确保您的src文件位于example_package_YOUR_USERNAME_HERE中(这是文档中使用的示例包名称),而不是src。错误地将文件放在src中可能会产生问题,就像问题描述中所说的那样。
参考:https://packaging.python.org/en/latest/tutorials/packaging-projects/ 包应该设置如下:
packaging_tutorial/
└── src/
    └── example_package_YOUR_USERNAME_HERE/
        ├── __init__.py
        └── example.py

1

我曾经遇到过同样的问题,升级setuptools可以解决:

python3 -m pip install --upgrade pip setuptools wheel

之后,重新安装该软件包,应该就能正常工作了 :)


1
对我来说,如果你这样做,我注意到了一些奇怪的事情:


# Not in the setup.py directory
python /path/to/folder/setup.py bdist_wheel

当您安装wheel时,它只会在您的site-packages文件夹中安装.dist-info文件夹。
但是,如果您执行以下操作:
cd /path/to/folder \
&& python setup.py bdist_wheel

这个轮子将包含你的所有文件。


1
在 Windows 上,使用 Python 3.9,这个命令对我也有效:py setup.py bdist_wheel - bbartling

0
如果您使用的是Windows 10+,确保您已经安装了所有正确的软件包,一种方法是单击左下角的“开始”按钮,搜索cmd.exe并右键单击“命令提示符”(确保选择“以管理员身份运行”)。输入“cd path to your Python 3.X installation”,您可以在文件资源管理器中找到此路径(转到Python安装的文件夹),然后在顶部复制它,并将其放在我上面写的path to your Python 3.X installation位置。完成后,点击回车键,输入“python -m pip install package”(package表示您想要安装的软件包)。现在,您的Python程序应该可以完美地工作了。

希望这可以解决你的问题,你能否提供一个更具体的例子? - Arnav Poddar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接