6

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

如果通过 pypi 从源代码、easy_install、pip 安装,是否有办法检索其路径?

非常感谢!

4

3 回答 3

4

默认情况下 distutilsbuild/在当前工作目录中创建,但可以通过参数更改--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 = "."

这个朴素的解析器版本仍然比optparse' 的版本短,对未知标志进行了适当的错误处理。您也可以使用argparse具有try_parse方法的解析器。

于 2012-10-15T15:10:25.873 回答
3

distutils/setuptools 提供了一个抽象Command类,用户可以使用该类将自定义命令添加到其包的设置过程中。这是与内置设置命令类似的同一类,build并且install是其子类。

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

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

例如,build该类在其方法中定义build_basebuild_lib选项,然后从其方法initialize_options中的命令行参数计算它们的值。这些类还在其方法中定义了和选项,但它从其方法中的命令中获取这些选项的值。finalize_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)

或者,如果您只想扩展它的功能并且它已经具有您需要的选项,您可以只继承 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)

Unfortunately, none of this is very well documented anywhere. I learned most of it from reading the distutils and setuptools source code. Any of the build*.py and install*.py files in either repository's command directory is informative. The abstract Command class is defined in distutils.

于 2021-09-10T18:48:09.057 回答
1

也许是这样的?在我的情况下适用于 python 3.8

...
from distutils.command.build import get_platform
import sys
import os
...
def configuration(parent_package='', top_path=None):

    config = Configuration('', parent_package, top_path)
    # add xxx library
    config.add_library('xxx',['xxx/src/fil1.F90',
                            'xxx/src/file2.F90',
                            'xxx/src/file3.F90'],
                            language='f90')

    # check for the temporary build directory option
    _tempopt = None
    _chkopt = ('-t','--build-temp')
    for _opt in  _chkopt:
        if _opt in sys.argv:
            _i = sys.argv.index(_opt)
            if _i < len(sys.argv)-1:
                _tempopt = sys.argv[_i+1]
                break
    # check for the base directory option
    _buildopt = 'build'
    _chkopt = ('-b','--build-base')
    for _opt in  _chkopt:
        if _opt in sys.argv:
            _i = sys.argv.index(_opt)
            if _i < len(sys.argv)-1:
                _buildopt = sys.argv[_i+1]
                break    

    if _tempopt is None:
        # works with python3 (check distutils/command/build.py)
        platform_specifier = ".%s-%d.%d" % (get_platform(), *sys.version_info[:2])
        _tempopt = '%s%stemp%s'%(_buildopt,os.sep,platform_specifier)

    # add yyy module (wraps fortran code in xxx library)
    config.add_extension('fastpost',sources=['yyy/src/fastpost.f90'],
                          f2py_options=['--quiet'],
                          libraries=['xxx'])

    # to access the mod files produced from fortran modules comppilaton, add 
    # the temp build directory to the include directories of the configuration
    config.add_include_dirs(_tempopt)
    return config

setup(name="pysimpp",
      version="0.0.1",
      description="xxx",
      author="xxx",
      author_email="xxx@yyy",
      configuration=configuration,)

于 2021-04-06T00:09:26.913 回答