从 setup.py 中获取“build”目录的可靠方法

Lio*_*her 5 python distutils numpy setuptools setup.py

在 setup.py 脚本中,我需要为安装创建一些临时文件。放置它们的自然位置是“build/”目录。

如果通过pypi,从源,easy_install,pip,...安装,有没有办法检索其工作路径?

非常感谢!

ala*_*lan 5

distutils/setuptools 提供了一个抽象Command类,用户可以使用该抽象类将自定义命令添加到其包的设置过程中。该类与内置安装命令 (如 )buildinstall是其子类。

作为抽象类的子类的每个类都Command必须实现initialize_optionsfinalize_optionsrun方法。这些方法名称引用的“选项”是从用户提供的命令行参数派生的类属性(它们也可以具有默认值)。方法initialize_options是定义类选项的地方,finalize_options方法是分配类选项值的地方,方法run是使用类选项值执行命令功能的地方。

由于命令行参数可能影响多个命令,因此某些命令类可能与其他命令类共享选项。例如,所有 distutils/setuptools 构建命令(buildbuild_pybuild_clibbuild_extbuild_scripts)以及install命令都需要知道构建目录在哪里。不是让这些命令类中的每一个都定义并将相同的命令行参数解析为相同的选项,而是build作为所有这些命令中要执行的第一个命令的命令定义并解析命令行参数和选项,并且所有其他类build从其方法中的命令获取选项值finalize_options

例如,该类在其方法中build定义build_base和选项,然后根据其方法中的命令行参数计算它们的值。这些类还在其方法中定义了和选项,但它从其方法中的命令获取这些选项的值。build_libinitialize_optionsfinalize_optionsinstallbuild_basebuild_libinitialize_optionsbuildfinalize_options

您可以使用相同的模式将自定义子命令添加到命令中,build如下所示(与 类似install

import setuptools
from distutils.command.build import build


class BuildSomething(setuptools.Command):

  def initialize_options(self):
    # define the command's options
    self.build_base = None
    self.build_lib = None
  
  def finalize_options(self):
    # get the option values from the build command
    self.set_undefined_options('build',
                               ('build_base', 'build_base'),
                               ('build_lib', 'build_lib'))

  def run(self):
    # do something with the option values
    print(self.build_base)  # defaults to 'build'
    print(self.build_lib)


build_something_command = 'build_something'


class Build(build):

  def has_something(self):
    # update this to check if your build should run
    return True

  sub_commands =  [(build_something_command, has_something)] + build.sub_commands


COMMAND_CLASS = {
  build_something_command: BuildSomething,  # custom command
  'build': Build  # override distutils/setuptools build command
}


setuptools.setup(cmdclass=COMMAND_CLASS)
Run Code Online (Sandbox Code Playgroud)

或者,如果您只想扩展其功能并且它已经具有您需要的选项,您可以只对 distutils/setuptools 类之一进行子类化

import setuptools
from setuptools.command.build_py import build_py


class BuildPy(build_py):

  def initialize_options(self):
    pass
  
  def finalize_options(self):
    pass

  def run(self):
    # do something with the option values
    print(self.build_lib)  # inherited from build_py
    build_py.run(self)  # make sure the regular build_py still runs


COMMAND_CLASS = {
  'build_py': BuildPy  # override distutils/setuptools build_py command
}


setuptools.setup(cmdclass=COMMAND_CLASS)
Run Code Online (Sandbox Code Playgroud)

不幸的是,这些都没有得到很好的记录。我通过阅读distutilssetuptools源代码学到了大部分内容。任一存储库目录中的任何build*.py和文件都是信息丰富的。抽象类在 distutils 中定义install*.pycommandCommand


mec*_*ind 4

默认情况下 distutils 在当前工作目录中创建build/,但可以通过参数更改--build-base。似乎 distutils 在执行时解析它setup,并且解析的参数无法从外部访问,但你可以自己剪切它:

import sys
build_base_long = [arg[12:].strip("= ") for arg in sys.argv if arg.startswith("--build-base")]
build_base_short = [arg[2:].strip(" ") for arg in sys.argv if arg.startswith("-b")]
build_base_arg = build_base_long or build_base_short
if build_base_arg:
    build_base = build_base_arg[0]
else:
    build_base = "."
Run Code Online (Sandbox Code Playgroud)

这个简单版本的解析器仍然比optparse对未知标志进行正确错误处理的版本短。您也可以使用argparse的解析器,它有try_parse方法。