【发布时间】:2010-10-15 05:20:51
【问题描述】:
请提示我如何将用户定义的参数从命令行和 setup.cfg 配置文件传递到 distutils 的 setup.py 脚本。我想编写一个 setup.py 脚本,它接受我的包特定参数。例如:
python setup.py install -foo myfoo
【问题讨论】:
请提示我如何将用户定义的参数从命令行和 setup.cfg 配置文件传递到 distutils 的 setup.py 脚本。我想编写一个 setup.py 脚本,它接受我的包特定参数。例如:
python setup.py install -foo myfoo
【问题讨论】:
由于 Setuptools/Distuils 的文档非常糟糕,我自己在寻找答案时遇到了问题。但最终我偶然发现了this 的例子。此外,this 类似的问题也很有帮助。基本上,带有选项的自定义命令如下所示:
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,
}
)
【讨论】:
install 行为更改为新命令。在我看来,OP 想要修改 install 命令,以便它接受一个新参数而不是完全替换它。
foo=与变量self.foo相关?通过查看更复杂的示例,我发现人们用下划线交换连字符;所以像input-dir= 这样的东西变成self.input_dir。这一切如何运作?我会很感激解释这一点。
user_options?你如何扩展它?
sdist 的源代码很有启发性。 github.com/python/cpython/blob/master/Lib/distutils/command/… 它回答了上面的许多问题,例如多个参数等。@JakeStevens-Haas,第二个参数用于替代参数形式,因此如果您想使用 -t 和 --token,那么您可以将附加参数指定为 @ 987654334@
这是一个非常简单的解决方案,您所要做的就是过滤掉 sys.argv 并在调用 distutils setup(..) 之前自己处理它。
像这样的:
if "--foo" in sys.argv:
do_foo_stuff()
sys.argv.remove("--foo")
...
setup(..)
关于如何使用 distutils 执行此操作的文档很糟糕,最终我遇到了这个:the hitchhikers guide to packaging,它使用了sdist 及其user_options。
我发现 extending distutils 参考资料不是特别有用。
虽然这看起来像是使用 distutils 的“正确”方式(至少是我能找到的唯一一种模糊记录的方式)。我在另一个答案中提到的--with 和--without 开关上找不到任何东西。
这个 distutils 解决方案的问题在于它对我正在寻找的东西来说太复杂了(你可能也是这种情况)。
添加几十行和子类sdist 对我来说是错误的。
【讨论】:
install_requires,那就太好了
是的,现在是 2015 年,在 setuptools 和 distutils 中添加命令和选项的文档仍然很大程度上缺失。
经过几个令人沮丧的小时后,我想出了以下代码,用于向setup.py 的install 命令添加自定义选项:
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,
},
:
【讨论】:
custom_option= 更改为custom-option 时,可以使用--custom-option=bar 作为参数。确切的错误是distutils.errors.DistutilsGetoptError: invalid long option name 'custom_option' (must be letters, numbers, hyphens only
您不能真正将自定义参数传递给脚本。但是,以下事情是可能的并且可以解决您的问题:
--with-featurename 启用可选功能,使用--without-featurename 可以禁用标准功能。 [AFAIR 这需要安装工具]set,而在 linux/OS X (FOO=bar python setup.py) 上可以使用前缀。cmd_classes 扩展 distutils,它可以实现新功能。它们也是可链接的,因此您可以使用它来更改脚本中的变量。 (python setup.py foo install) 将在执行install 之前执行foo 命令。希望能有所帮助。一般来说,我建议提供更多信息,您的额外参数到底应该做什么,也许有更好的解决方案。
【讨论】:
--with-featurename 标志。以防万一其他人偶然发现这个......
我成功地使用了一种解决方法来使用类似于 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(...)
可以添加一些额外的验证以确保输入正确,但我就是这样做的
【讨论】:
ArgumentParser.parse_known_arguments返回的第二个值获得。这是首选,因为它还将处理非位置的未使用 args(从而避免关于 setuptools 的假设)。使用argparse 会好很多。此外,用未使用的 args 替换 sys.argv 并不比弹出位置 args 更多。他们都只是简单地删除消耗的参数,以“将剩余的参数传递给另一个脚本或程序”。
类似于 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 获得常规的 setup.py 帮助(前提是给出了--foo)。
【讨论】:
Retracted in favour of the answer given by @Cerin?
也许你是一个像我一样没有经验的程序员,在阅读了上面所有的答案后仍然在挣扎。因此,您可能会发现另一个可能有用的示例(并解决先前答案中有关输入命令行参数的 cmets):
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 方法确实选择了正确的参数。
我认为有用的其他资源(更多和更多示例):
https://seasonofcode.com/posts/how-to-add-custom-build-steps-and-commands-to-setuppy.html
【讨论】:
要与python setup.py install 和pip install . 完全兼容,您需要使用环境变量,因为pip 选项--install-option= 存在错误:
--install-option leaks across lines这是一个不使用--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
参考资料:
【讨论】:
pip 版本对以后看到这个答案的人没有帮助。
pip 和setuptools 错误作斗争的人非常有用。
pip 是否存在问题,而是一旦该问题在未来得到解决 - 比如说 - 这个答案将变得令人难以置信的混乱......那就是为什么你应该限定pip 的哪些版本受到/受到影响。这就是我要说的......
pip 20.2 中的 --install-option 将被删除。 github.com/pypa/pip/issues/7309