76

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

python setup.py install -foo myfoo
4

8 回答 8

77

由于 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,
    }
)
于 2013-11-27T17:17:29.920 回答
34

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

if "--foo" in sys.argv:
    do_foo_stuff()
    sys.argv.remove("--foo")
...
setup(..)

关于如何使用 distutils 执行此操作的文档非常糟糕,最终我遇到了这个:hitchhikers guide to packaging,它使用sdist和它的user_options. 我发现扩展 distutils参考并不是特别有用。

尽管这看起来像是使用 distutils 的“正确”方式(至少是我能找到的唯一一种模糊记录的方式)。我找不到其他答案中提到的任何东西--with和开关。--without

这个 distutils 解决方案的问题在于,它对于我正在寻找的东西来说太复杂了(你也可能是这种情况)。添加几十行和子类sdist对我来说是错误的。

于 2011-01-25T10:54:17.403 回答
23

setuptools是的,现在是 2015 年,在两者中添加命令和选项的文档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)

值得一提的是 install.run() 检查它是否被称为“本机”或已修补:

if not self._called_from_setup(inspect.currentframe()):
    orig.install.run(self)
else:
    self.do_egg_install()

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

setup(
    cmdclass={
        'install': InstallCommand,
    },
    :
于 2015-10-17T00:21:10.450 回答
13

您不能真正将自定义参数传递给脚本。但是,以下事情是可能的,并且可以解决您的问题:

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

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

于 2009-03-25T06:48:40.313 回答
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(...)

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

于 2015-02-04T19:29:01.533 回答
5

与 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

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

于 2013-09-22T04:39:08.190 回答
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!")

setup(
    # Some other omitted details
    cmdclass={
        'runClient': RunClientCommand,
    },

以上内容经过测试,来自我编写的一些代码。我还包含了更详细的文档字符串,以使事情更容易理解。

至于命令行: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

于 2016-04-08T00:11:34.520 回答
3

要与两者完全兼容,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',
        ...
)

然后,您可以在 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

但是,如果您在 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

参考:

  1. 如何使用“--install-option”从 pip 获取传递给 setup.py 的参数?
  2. 将命令行参数传递给 pip install
  3. 将库路径作为命令行参数传递给 setup.py
于 2019-05-24T02:32:27.300 回答