【问题标题】:How to include a text file in a python installed package?如何在 python 安装包中包含文本文件?
【发布时间】:2019-04-26 12:00:58
【问题描述】:

我创建了一个如下所示的 python 包:

/command
    /command
        module.py
        __main__.py
    README.md
    setup.py
    file.txt

安装我运行:

sudo python setup.py install

现在当我打电话时

$ command

它显示了这个错误:

FileNotFoundError: [Errno 2] No such file or directory: '../file.txt'

大概有setup.py__main__.pymodule.py

模块的内容

setup.py

import setuptools

setuptools.setup(
    name='command',
    ...
    entry_points={
        'console_scripts': [
            'command = command.__main__:main'
        ]
    }
)

__main__.py

from . import module

def main():
    module.run('arg')

module.py

import shutil

# copy file.txt from command project into the directory where it is running
def run(arg):
    shutil.copyfile('../file.txt', './file.txt')

通过以下方式安装此软件包后:

sudo python setup.py install

并在命令行调用程序

$ command

我收到以下错误

FileNotFoundError: [Errno 2] No such file or directory: '../file.txt'

如何查看和使用属于已安装包的文件,但要在运行该程序的环境中使用它?

编辑:

This a simplification of the problem you can download and test:

https://github.com/mctrjalloh/project_initializer

【问题讨论】:

  • 安装包中是否存在该文件?如果是这样,您可以在模块中使用__file__ 来找出该模块的绝对路径并从那里计算到数据文件的路径
  • @MichaelButscher 我不知道它是否在那里,因为它被打包在一个我无法打开的 .egg 文件中。
  • 上面似乎您已经尝试安装该软件包。所以你应该能够检查文件是否也安装了。
  • 我已经安装了这个包,安装没有任何错误。正是在使用它时才会出现错误。但是如果这就是您要问的,我如何检查 file.txt 是否在该已安装的软件包中..
  • 使用某种文件资源管理器或使用命令行列出安装目录(和子目录)中的文件。

标签: python include installation text-files setup.py


【解决方案1】:

包中默认只包含python文件。

要包含更多内容,请添加 MANIFEST.in 并列出该文件。 For example.

Here is a comprehensive tutorial

【讨论】:

  • 当您通过 python setup.py bdist_wheel 构建包时,diatutils 尊重 MANIFEST.in
【解决方案2】:

所以经过大量研究后,我找到了解决方案以及它是如何工作的。这有点令人困惑,其他 stackoverflow 答案都没有真正的解释性。我想在这里试试:

我制作了一个示例项目,仅用于演示和测试解决方案。我提出了两种解决方案:一种使用 setup() 函数的 data_files 参数,另一种使用我最喜欢的 package_data 参数。

Here is the link to the github repo you can download and test

安装运行后使用它

proj init <some-name>

但简而言之,每种方法都有最重要的模块。

使用 data_files= 参数方法:

项目结构:

project_initializer
    project_initializer
        __init__.py
        __main__.py
        init.py
    README.md
    setup.py

setup.py

import setuptools
import os
import sys


PROJECT_NAME = "project_initializer"
DATA_DIR = os.path.join(
    sys.prefix, "local/lib/python3.6/dist-packages", PROJECT_NAME)


setuptools.setup(
    name='project_initializer',
    version='0.1.0',
    packages=setuptools.find_packages(),
    install_requires=[
        'docopt'
    ],
    data_files=[         # is the important part
        (DATA_DIR, [
            "README.md",
            ".gitignore"
        ])               
    ],
    entry_points={
        'console_scripts': [
            'proj = project_initializer.__main__:main'
        ]
    }
)

init.py

import subprocess
import os
import shutil
import sys

"""Create a new project and initialize it with a .gitignore file
@params project: name of a project to be initialized
effects:
    /project
        README.md
    README.md in the created project directory must be the same as the README.md in THIS directory 
"""

PROJECT_NAME = "project_initializer"
DATA_DIR = os.path.join(
    sys.prefix, "local/lib/python3.6/dist-packages", PROJECT_NAME)


def run(project):
    os.mkdir(project)
    shutil.copyfile(os.path.join(DATA_DIR, "README.md"),
                    f"{project}/README.md")  # problem solved


if __name__ == '__main__':
    run("hello-world")

使用 package_data= ARGUMENT METHOD(我更喜欢)

项目结构:

project_initializer
    project_initializer
        data/
            README.md  # the file we want to copy
        __init__.py
        __main__.py
        init.py
    README.md
    setup.py

setup.py

import setuptools


setuptools.setup(
    name='project_initializer',
    version='0.1.0',
    packages=setuptools.find_packages(),
    package_dir={'project_initializer': 'project_initializer'}, # are the ... 
    package_data={'project_initializer': [ # ... important parameters
        'data/README.md', 'data/.gitignore']},
    install_requires=[
        'docopt'
    ],
    entry_points={
        'console_scripts': [
            'proj = project_initializer.__main__:main'
        ]
    }
)

init.py

import subprocess
import os
import shutil
import sys

PROJECT_DIR = os.path.dirname(__file__)

"""Create a new project and initialize it with a .gitignore file
@params project: name of a project to be initialized
effects:
    /project
        .gitignore
    .gitignore in the created project directory must be the same as the gitignore in THIS directory 
"""


def run(project):
    os.mkdir(project)
    shutil.copyfile(os.path.join(PROJECT_DIR, 'data/README.md'),
                    f"{project}/README.md")  # problem solved


if __name__ == '__main__':
    run("hello-world")

我更喜欢最后一种方法的原因是您不必在 setup.py 模块中导入任何内容,这可能是一种不好的做法。我想 setup.py 文件中不应该导入任何内容,因为它是主包的外部文件。

有关这两个参数之间差异的更详细说明,请查看 python 文档

Using data_files= argument

Using package_data= argument

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-06
    • 1970-01-01
    • 2019-10-19
    • 1970-01-01
    • 2020-07-05
    相关资源
    最近更新 更多