运行setup.py时,如何获取Python wheel的文件名?

时间:2018-08-20 22:23:13

标签: python cross-platform setup.py python-wheel

我有一个使用以下命令创建Python轮子的构建过程:

python setup.py bdist_wheel

构建过程可以在许多平台(Windows,Linux,py2,py3等)上运行,我想保留默认的输出名称(例如mapscript-7.2-cp27-cp27m-win_amd64.whl)以上传到PyPI。

是否仍然可以获取生成的转轮的文件名(例如mapscript-7.2-cp27-cp27m-win_amd64.whl)并保存到变量中,以便稍后可以在脚本中安装该转轮进行测试?

理想情况下,解决方案将是跨平台的。我当前的方法是尝试清除文件夹,列出所有文件,然后选择列表中的第一个(也是唯一一个)文件,但是,这似乎是一个非常棘手的解决方案。

3 个答案:

答案 0 :(得分:2)

setuptools

如果使用setup.py脚本构建Wheel发行版,则可以使用bdist_wheel命令查询Wheel文件名。这种方法的缺点是它使用bdist_wheel的私有API,因此如果作者决定更改它,则代码可能会在wheel软件包更新时中断。

from setuptools.dist import Distribution


def wheel_name(**kwargs):
    # create a fake distribution from arguments
    dist = Distribution(attrs=kwargs)
    # finalize bdist_wheel command
    bdist_wheel_cmd = dist.get_command_obj('bdist_wheel')
    bdist_wheel_cmd.ensure_finalized()
    # assemble wheel file name
    distname = bdist_wheel_cmd.wheel_dist_name
    tag = '-'.join(bdist_wheel_cmd.get_tag())
    return f'{distname}-{tag}.whl'

wheel_name函数接受与传递给setup()函数的参数相同的参数。用法示例:

>>> wheel_name(name="mydist", version="1.2.3")
mydist-1.2.3-py3-none-any.whl
>>> wheel_name(name="mydist", version="1.2.3", ext_modules=[Extension("mylib", ["mysrc.pyx", "native.c"])])
mydist-1.2.3-cp36-cp36m-linux_x86_64.whl

请注意,本机库(在上面的示例中为mysrc.pyxnative.c)的源文件不必存在来组装转轮名称。如果本机lib的源尚不存在,这很有用(例如,稍后您将通过SWIG,Cython或其他任何方式生成它们)。

这使得wheel_name可以在定义分发元数据的setup.py脚本中轻松重用:

# setup.py
from setuptools import setup, find_packages, Extension
from setup_helpers import wheel_name

setup_kwargs = dict(
    name='mydist',
    version='1.2.3',
    packages=find_packages(),
    ext_modules=[Extension(...), ...],
    ...
)
file = wheel_name(**setup_kwargs)
...
setup(**setup_kwargs)

如果要在设置脚本之外使用它,则必须自己整理对setup()参数的访问权限(例如,从setup.cfg脚本或任何其他内容中读取它们)。

这部分大致是基于我对setuptools, know in advance the wheel filename of a native library的其他回答

poetry

如果使用poetry,则可以简化很多事情(实际上是单行),因为所有相关的元数据都存储在pyproject.toml中。同样,这使用了未公开的API:

from clikit.io import NullIO

from poetry.factory import Factory
from poetry.masonry.builders.wheel import WheelBuilder
from poetry.utils.env import NullEnv


def wheel_name(rootdir='.'):
    builder = WheelBuilder(Factory().create_poetry(rootdir), NullEnv(), NullIO())
    return builder.wheel_filename

rootdir参数是包含pyproject.toml脚本的目录。

flit

AFAIK flit无法使用本机扩展名构建轮子,因此只能给您purelib名称。但是,如果您的项目使用flit进行发行版本构建,则可能会很有用。请注意,这也使用了未公开的API:

from flit_core.wheel import WheelBuilder
from io import BytesIO
from pathlib import Path


def wheel_name(rootdir='.'):
    config = str(Path(rootdir, 'pyproject.toml'))
    builder = WheelBuilder.from_ini_path(config, BytesIO())
    return builder.wheel_filename

实施自己的解决方案

我不确定是否值得。不过,如果您要选择此路径,请在发现一些旧的已弃用的内容之前,考虑使用packaging.tags,甚至决定自己查询平台。不过,您仍然必须依靠私人工具来组装正确的车轮名称。

答案 1 :(得分:1)

我使用了霍夫林解决方案的修改版。我的目标是将构建文件复制到“最新”文件中。 setup()函数将返回一个对象,其中包含您需要的所有信息,因此您可以找出它的实际构造,这似乎比上面的解决方案更简单。假设您正在使用变量version,以下代码将获取我刚刚构建的文件名,然后将其复制。

setup = setuptools.setup(
    # whatever options you currently have
    )
wheel_built = 'dist/{}-{}.whl'.format(
    setup.command_obj['bdist_wheel'].wheel_dist_name,
    '-'.join(setup.command_obj['bdist_wheel'].get_tag()))
wheel_latest = wheel_built.replace(version, 'latest')
shutil.copy(wheel_built, wheel_latest)
print('Copied {} >> {}'.format(wheel_built, wheel_latest))

我猜一个可能的缺点是您必须实际进行构建才能获得名称,但是由于那是我工作流程的一部分,因此我对此表示满意。 hoefling的解决方案的好处是无需进行构建即可让您计划名称,但这似乎更为复杂。

答案 2 :(得分:0)

我目前安装轮子的方法是将pip指向包含轮子的文件夹,然后让它自己搜索:

xmlboiler/core/data/assets/

twine也可以直接指向文件夹,而无需知道确切的车轮名称。