在Python中以编程方式确定distutils数据文件的位置

12 python packaging distutils python-module setuptools

我正在尝试将数据文件包含在我的包中的distutils中,然后使用相对路径引用它们(参见http://docs.python.org/distutils/setupscript.html#distutils-additional-files)

我的目标结构是:

myproject/
  mycode.py
  data/
    file1.dat
Run Code Online (Sandbox Code Playgroud)

代码mycode.py,实际上是包中的脚本.它依赖于访问data/file1.dat,使用相对路径引用它.在setup.py,我有:

setup(
 ...
 scripts = "myproject/mycode.py"
 data_files = [('data', 'myproject/data/file1.dat')]
)
Run Code Online (Sandbox Code Playgroud)

假设用户现在使用:

python setup.py --prefix=/home/user/
Run Code Online (Sandbox Code Playgroud)

然后mycode.py会出现在某些地方/home/user/bin/.但是data/file1.dat现在已经破坏了引用,因为脚本存在于数据的其他地方.

我怎样才能找到mycode.py绝对路径myproject/data/file1.dat,所以我可以根据用户安装包的位置正确引用它?

编辑
当我安装这个prefix=/home/user/,我得到data/file1.dat创造了/home/user/这正是我想要的,唯一缺少的部分是如何检索,只给出一个相对路径,不知道在用户安装的程序包的绝对路径该文件编程.当我尝试使用package_data而不是data_files,它不起作用 - 我只是不在data/file1.dat任何地方创建,即使我删除我的MANIFEST文件.

我已经阅读了目前关于这个看似非常普遍的问题的所有讨论.然而,所有提出的解决方案都没有处理我上面的情况,其中需要访问的代码data_files是脚本,其位置可能会根据--prefix参数而改变setup.py.我能想到的解决这个问题的唯一黑客是添加数据文件scripts=setup(),如:

setup(
  ...
  scripts = ["myproject/mycode.py", "myproject/data/file1.data"]
)
Run Code Online (Sandbox Code Playgroud)

这是一个可怕的黑客,但它是我能想到的唯一方法,以确保file1.data将与脚本定义在同一个地方scripts=,因为我找不到任何平台独立和安装敏感的API来恢复data_files用户运行后的位置setup.py install(可能与--prefix=args).

Jas*_*mbs 11

我认为混淆源于脚本的使用.脚本应该引用一个可运行的可执行文件,可能是与您的软件包相关的实用程序脚本,也可能是您的软件包功能的入口点.在任何一种情况下,您都应该期望任何脚本不会与包的其余部分一起安装.这种期望主要是由于包被认为是库(并安装到lib目录)的惯例,而脚本被认为是可执行文件(并安装到bin或Scripts目录).此外,数据文件既不是可执行文件也不是库,而是完全独立的.

因此,从脚本中,您需要确定数据文件的位置.根据Python文档,

如果directory是相对路径,则相对于安装前缀进行解释.

因此,您应该在mycode脚本中编写类似以下内容的内容来查找数据文件:

import sys
import os

def my_func():
    with open(os.path.join(sys.prefix, 'data', 'file1.dat')) as f:
        print(next(f))

if __name__ == '__main__':
    my_func()
Run Code Online (Sandbox Code Playgroud)

如果你对代码和数据没有捆绑在一起的方式不满意(我不会),那么我会重构你的包,这样你就有了一个实际的Python包(和模块)并使用packages =和package_data =将数据注入包中,然后创建一个调用包中模块的简单脚本.

我通过创建这个树来做到这一点:

.
?   setup.py
?
????myproject
?   ?   mycode.py
?   ?   __init__.py
?   ?
?   ????data
?           file1.dat
?
????scripts
        run-my-code.py
Run Code Online (Sandbox Code Playgroud)

使用setup.py:

from distutils.core import setup

setup(
    name='myproject',
    version='1.0',
    scripts=['scripts/run-my-code.py'],
    packages=['myproject'],
    package_data = {
        'myproject': ['data/file1.dat'],
    },
)
Run Code Online (Sandbox Code Playgroud)

run-my-code.py简单地说:

from myproject import mycode

mycode.my_func()
Run Code Online (Sandbox Code Playgroud)

__init__ 是空的,mycode.py看起来像:

import os

here = os.path.dirname(__file__)

def my_func():
    with open(os.path.join(here, 'data', 'file1.dat')) as f:
        print(next(f))
Run Code Online (Sandbox Code Playgroud)

后一种方法将数据和代码捆绑在一起(在site-packages/myproject中),并且只将脚本安装在不同的位置(因此它显示在$ PATH中).


Jen*_*man 5

您应该能够使用pkg_resources.resource_filename来获取data_files中文件的文件名。

  • 这不适用于通过`data_files`安装的文件。它适用于通过package_data安装的文件。 (11认同)
  • 是的,这是要走的路。这就像疲惫的印第安纳琼斯遇到一个可以与之战斗的人,因为他手头有枪,他只是向他开枪。 (2认同)