distutils:如何将用户定义的参数传递给setup.py?

63 python distutils setup.py

请提示我如何将命令行和setup.cfg配置文件中的用户定义参数传递给distutils的setup.py脚本.我想编写一个setup.py脚本,它接受我的包特定参数.例如:

python setup.py install -foo myfoo
Run Code Online (Sandbox Code Playgroud)

谢谢你,
Mher

Cer*_*rin 59

由于Setuptools/Distuils的记录非常糟糕,我自己也找不到答案.但最终我偶然发现了这个例子.此外,这个类似的问题很有帮助.基本上,带有选项的自定义命令如下所示:

from distutils.core import setup, Command

class InstallCommand(Command):
    description = "Installs the foo."
    user_options = [
        ('foo=', None, 'Specify the foo to bar.'),
    ]
    def initialize_options(self):
        self.foo = None
    def finalize_options(self):
        assert self.foo in (None, 'myFoo', 'myFoo2'), 'Invalid foo!'
    def run(self):
        install_all_the_things()

setup(
    ...,
    cmdclass={
        'install': InstallCommand,
    }
)
Run Code Online (Sandbox Code Playgroud)

  • 嗨,你运行的命令行是什么传递给foo =什么? (5认同)
  • 问题是它似乎将“安装”行为更改为新命令。在我看来,OP 想要修改安装命令,以便它接受新参数而不是完全替换它。 (2认同)
  • 这里有我不明白的地方。程序如何知道字符串`foo=` 与变量`self.foo` 相关?通过查看更复杂的例子,我看到人们用下划线交换连字符;所以像`input-dir=`这样的东西变成了`self.input_dir`。这一切是如何运作的?我将不胜感激解释这一点。 (2认同)
  • 这对我很有用,但是如何指定多个`user_options`?你如何扩展它? (2认同)
  • 其中很多问题都已经过时了,但我发现阅读“sdist”的源代码非常有启发性。https://github.com/python/cpython/blob/master/Lib/distutils/command/sdist.py 它回答了上面的许多问题,例如多个参数等。@JakeStevens-Haas,第二个参数用于替代参数形式,因此,如果您想使用“-t”和“--token”,那么您可以将附加参数指定为“('token=', 't', 'Token Documentation.')” (2认同)

tot*_*aam 26

这是一个非常简单的解决方案,您需要做的就是sys.argv在调用distutils之前过滤掉并自己处理它setup(..).像这样的东西:

if "--foo" in sys.argv:
    do_foo_stuff()
    sys.argv.remove("--foo")
...
setup(..)
Run Code Online (Sandbox Code Playgroud)

有关如何使用distutils执行此操作的文档非常糟糕,最终我遇到了这个问题:搭便车指导包装,使用sdist和使用它user_options.我发现扩展的distutils参考不是特别有用.

虽然这看起来像是使用distutils的"正确"方式(至少是我能找到的唯一一个模糊记录的方法).我在其他答案中找不到任何东西--with--without开关.

这种distutils解决方案的问题在于它只是涉及我正在寻找的东西(这也可能是你的情况).添加几十行和子类化sdist对我来说是错误的.

  • 这个解决方案不正确,因为--foo可能用于另一个命令:使用"setup.py build_ext --inplace --foo install",安装不应该认为它有--foo. (5认同)
  • 我不知道你为什么要给我一个我希望能做的例子.我从未声称这是一个解决方案,为什么说这不正确?我提供了关于这个主题的唯一文档的指示,说它"不像通常认为的那么难"不能帮助我们找到更好的答案. (2认同)

Ron*_*zer 16

是的,它是2015年在这两个添加命令和选项的文件setuptools,并distutils在很大程度上仍然下落不明.

经过几个令人沮丧的时间后,我想出了以下代码,用于为以下install命令添加自定义选项setup.py:

from setuptools.command.install import install


class InstallCommand(install):
    user_options = install.user_options + [
        ('custom_option=', None, 'Path to something')
    ]

    def initialize_options(self):
        install.initialize_options(self)
        self.custom_option = None

    def finalize_options(self):
        #print('The custom option for install is ', self.custom_option)
        install.finalize_options(self)

    def run(self):
        global my_custom_option
        my_custom_option = self.custom_option
        install.run(self)  # OR: install.do_egg_install(self)
Run Code Online (Sandbox Code Playgroud)

值得一提的是,install.run()会检查它是"本机"调用还是已修补:

if not self._called_from_setup(inspect.currentframe()):
    orig.install.run(self)
else:
    self.do_egg_install()
Run Code Online (Sandbox Code Playgroud)

此时,您可以使用以下命令注册命令setup:

setup(
    cmdclass={
        'install': InstallCommand,
    },
    :
Run Code Online (Sandbox Code Playgroud)

  • 应该注意的是,这个InstallCommnad是在调用setup()之后运行的。这意味着,您无法在 setup() 命令之前控制任何内容,例如根据用户参数构建扩展。有人知道该怎么做吗?就像说明 cython 扩展是否应该使用 openmp 一样? (3认同)

Arm*_*her 11

您无法将自定义参数传递给脚本.但是,以下事情是可能的,可以解决您的问题:

  • 可以使用可选功能--with-featurename,可以禁用标准功能--without-featurename.[AFAIR这需要setuptools]
  • 你可以使用环境变量,但这些变量需要set在windows上,而前缀可以在linux/OS X(FOO=bar python setup.py)上使用.
  • 您可以使用自己的cmd_classes 扩展distutils,这可以实现新功能.它们也是可链接的,因此您可以使用它来更改脚本中的变量.(python setup.py foo install)将在foo执行之前执行该命令install.

希望有所帮助.一般来说,我建议提供更多信息,确切地说您的额外参数应该做什么,也许有更好的解决方案可用.

  • 我在 *setuptools* 中没有遇到任何 `--with-featurename` 标志。以防万一其他人偶然发现这个...... (3认同)

小智 9

我成功地使用了一种解决方法来使用类似于 totaam 建议的解决方案。我最终从 sys.argv 列表中弹出了我的额外参数:

import sys
from distutils.core import setup
foo = 0
if '--foo' in sys.argv:
    index = sys.argv.index('--foo')
    sys.argv.pop(index)  # Removes the '--foo'
    foo = sys.argv.pop(index)  # Returns the element after the '--foo'
# The foo is now ready to use for the setup
setup(...)
Run Code Online (Sandbox Code Playgroud)

可以添加一些额外的验证以确保输入良好,但这就是我所做的

  • 这有点像黑客,但它有效并且相对容易理解。人们可以通过利用 argparse 并将 sys.argv 替换为 argparse 中的位置参数(并使用您想要的任何关键字参数)来完成类似的事情。这将是一个更大的黑客攻击,但允许人们利用 argparse。 (2认同)
  • 更好的是,剩余的未使用参数可以作为从 [`ArgumentParser.parse_known_arguments`](https://docs.python.org/2/library/argparse.html#partial-parsing) 返回的第二个值获得。这是首选,因为它还将处理非位置的未使用参数(从而避免对“setuptools”的假设)。使用`argparse`要好得多。此外,用未使用的 args 替换 `sys.argv` 并不比弹出位置 args 更像是一种黑客攻击。它们都只是删除消耗的参数,以“将剩余的参数传递给另一个脚本或程序”。 (2认同)

and*_*rew 7

与 totaam 给出的类似的快速简单的方法是使用 argparse 来获取 -foo 参数,并将其余参数留给 distutils.setup() 的调用。恕我直言,为此使用 argparse 比手动迭代 sys.argv 更好。例如,将其添加到 setup.py 的开头:

argparser = argparse.ArgumentParser(add_help=False)
argparser.add_argument('--foo', help='required foo argument', required=True)
args, unknown = argparser.parse_known_args()
sys.argv = [sys.argv[0]] + unknown
Run Code Online (Sandbox Code Playgroud)

add_help=False参数意味着您仍然可以使用常规 setup.py 帮助-h(前提--foo是给出)。

  • 为什么“撤回支持@Cerin 给出的答案”? (2认同)

Oth*_*ire 5

也许您像我一样是一个没有经验的程序员,但在阅读完以上所有答案后仍然感到吃力。因此,您可能会发现另一个可能有用的示例(并解决了前面有关输入命令行参数的答案中的注释):

class RunClientCommand(Command):
    """
    A command class to runs the client GUI.
    """

    description = "runs client gui"

    # The format is (long option, short option, description).
    user_options = [
        ('socket=', None, 'The socket of the server to connect (e.g. '127.0.0.1:8000')',
    ]

    def initialize_options(self):
        """
        Sets the default value for the server socket.

        The method is responsible for setting default values for
        all the options that the command supports.

        Option dependencies should not be set here.
        """
        self.socket = '127.0.0.1:8000'

    def finalize_options(self):
        """
        Overriding a required abstract method.

        The method is responsible for setting and checking the 
        final values and option dependencies for all the options 
        just before the method run is executed.

        In practice, this is where the values are assigned and verified.
        """
        pass

    def run(self):
        """
        Semantically, runs 'python src/client/view.py SERVER_SOCKET' on the
        command line.
        """
        print(self.socket)
        errno = subprocess.call([sys.executable, 'src/client/view.py ' + self.socket])
        if errno != 0:
            raise SystemExit("Unable to run client GUI!")
Run Code Online (Sandbox Code Playgroud)
setup(
    # Some other omitted details
    cmdclass={
        'runClient': RunClientCommand,
    },
Run Code Online (Sandbox Code Playgroud)

上面的代码已经过测试,并且是我编写的一些代码。我还提供了一些更详细的文档字符串,以使事情更容易理解。

至于命令行:python setup.py runClient --socket=127.0.0.1:7777。使用print语句进行的快速仔细检查表明,run方法确实选择了正确的参数。

其他资源,我发现有用的(更多更多的例子):

自定义distutils命令

https://seasonofcode.com/posts/how-to-add-custom-build-steps-and-commands-to-setuppy.html


use*_*ser 5

为了与两者完全兼容,python setup.py installpip install .需要使用环境变量,因为pip选项--install-option=有问题:

  1. pip--install-option跨行泄漏
  2. 确定应该对 Wheels 的 --(install|global)-option 做什么
  3. pip 未正确命名 abi3 轮子

这是一个不使用以下内容的完整示例--install-option

import os
environment_variable_name = 'MY_ENVIRONMENT_VARIABLE'
environment_variable_value = os.environ.get( environment_variable_name, None )

if environment_variable_value is not None:
    sys.stderr.write( "Using '%s=%s' environment variable!\n" % (
            environment_variable_name, environment_variable_value ) )

setup(
        name = 'packagename',
        version = '1.0.0',
        ...
)
Run Code Online (Sandbox Code Playgroud)

然后,你可以在 Linux 上像这样运行它:

MY_ENVIRONMENT_VARIABLE=1 pip install .
MY_ENVIRONMENT_VARIABLE=1 pip install -e .
MY_ENVIRONMENT_VARIABLE=1 python setup.py install
MY_ENVIRONMENT_VARIABLE=1 python setup.py develop
Run Code Online (Sandbox Code Playgroud)

但是,如果您使用的是 Windows,请像这样运行:

set "MY_ENVIRONMENT_VARIABLE=1" && pip install .
set "MY_ENVIRONMENT_VARIABLE=1" && pip install -e .
set "MY_ENVIRONMENT_VARIABLE=1" && python setup.py install
set "MY_ENVIRONMENT_VARIABLE=1" && python setup.py develop
Run Code Online (Sandbox Code Playgroud)

参考:

  1. 如何使用“--install-option”获取从 pip 传递给 setup.py 的参数?
  2. 将命令行参数传递给 pip install
  3. 将库路径作为命令行参数传递给 setup.py

  • “搞砸”而不给出确实搞乱的“pip”版本对于将来看到这个答案的人没有帮助。 (2认同)
  • 我在这里的观点并不是“pip”是否存在问题,而是事实上,一旦这个问题在未来得到解决——比如说——这个答案将变得令人难以置信......这就是为什么你应该限定什么版本) 的 `pip` 受到影响。这就是我要说的... (2认同)