当 运行 setup.py 时,如何获取 Python 车轮的文件名?
How do you get the filename of a Python wheel when running setup.py?
我有一个使用以下命令创建 Python 轮子的构建过程:
python setup.py bdist_wheel
构建过程在许多平台上可以是 运行(Windows、Linux、py2、py3 等),我想保留默认的输出名称(例如mapscript-7.2-cp27-cp27m-win_amd64.whl
) 上传到 PyPI。
有没有办法获取生成的 wheel 的文件名(例如 mapscript-7.2-cp27-cp27m-win_amd64.whl
)并保存到一个变量中,这样我就可以稍后在脚本中安装 wheel 进行测试?
理想情况下,解决方案是跨平台的。我目前的方法是尝试清除文件夹,列出所有文件和 select 列表中的第一个(也是唯一一个)文件,但这似乎是一个非常 hacky 的解决方案。
我目前安装 wheel 的方法是将 pip 指向包含 wheel 的文件夹并让它自己搜索:
python -m pip install --no-index --find-links=build/dist mapscript
twine 也可以直接指向文件夹,而无需知道确切的 wheel 名称。
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.pyx
或 native.c
)不必存在于 assemble 车轮名称中。这在本机库的源尚不存在的情况下很有用(例如,您稍后通过 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()
args 的访问(例如从 setup.cfg
脚本或其他什么读取它们)。
这部分大致基于我对
的其他回答
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
,然后再找到一些旧的已弃用的东西,甚至决定自己查询平台。不过,您仍然需要回退到私有内容以 assemble 正确的车轮名称。
我使用了 hoefling 解决方案的修改版本。我的目标是将构建复制到 "latest" wheel 文件。 setup()
函数将 return 一个对象,其中包含您需要的所有信息,因此您可以找出它实际构建的内容,这似乎比上面的解决方案更简单。假设你有一个变量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 的解决方案的好处是可以让您在不进行构建的情况下规划名称,但它似乎更复杂。
我有一个使用以下命令创建 Python 轮子的构建过程:
python setup.py bdist_wheel
构建过程在许多平台上可以是 运行(Windows、Linux、py2、py3 等),我想保留默认的输出名称(例如mapscript-7.2-cp27-cp27m-win_amd64.whl
) 上传到 PyPI。
有没有办法获取生成的 wheel 的文件名(例如 mapscript-7.2-cp27-cp27m-win_amd64.whl
)并保存到一个变量中,这样我就可以稍后在脚本中安装 wheel 进行测试?
理想情况下,解决方案是跨平台的。我目前的方法是尝试清除文件夹,列出所有文件和 select 列表中的第一个(也是唯一一个)文件,但这似乎是一个非常 hacky 的解决方案。
我目前安装 wheel 的方法是将 pip 指向包含 wheel 的文件夹并让它自己搜索:
python -m pip install --no-index --find-links=build/dist mapscript
twine 也可以直接指向文件夹,而无需知道确切的 wheel 名称。
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.pyx
或 native.c
)不必存在于 assemble 车轮名称中。这在本机库的源尚不存在的情况下很有用(例如,您稍后通过 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()
args 的访问(例如从 setup.cfg
脚本或其他什么读取它们)。
这部分大致基于我对
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
,然后再找到一些旧的已弃用的东西,甚至决定自己查询平台。不过,您仍然需要回退到私有内容以 assemble 正确的车轮名称。
我使用了 hoefling 解决方案的修改版本。我的目标是将构建复制到 "latest" wheel 文件。 setup()
函数将 return 一个对象,其中包含您需要的所有信息,因此您可以找出它实际构建的内容,这似乎比上面的解决方案更简单。假设你有一个变量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 的解决方案的好处是可以让您在不进行构建的情况下规划名称,但它似乎更复杂。