Я хочу использовать модуль argparse
для установки значения URL-адреса сервера. Помимо установки через аргумент, значение также может быть в переменной среды, хотя аргумент всегда должен иметь приоритет.
Для этого я добавил в синтаксический анализатор аргумент со значением переменной среды по умолчанию и настраиваемым типом. Однако, если аргумент опущен, пользовательский type=EnvironDefault
не инициализируется. Это означает, что если значение по умолчанию равно None
, это значение используется независимо, а не возникает исключение, как ожидалось.
Я нашел 10-летний вопрос, который предполагает, что необходимо проверить значение аргумента после разбора аргументов, но я надеюсь, что тем временем решение было добавлено к argparse
.
foo@bar:~$ export SERVER_URL = "https://some.server.co.uk"
foo@bar:~$ python mycli
Namespace(url='https://some.server.co.uk')
foo@bar:~$ export SERVER_URL = "https://some.server.co.uk"
foo@bar:~$ python mycli --url "https://different.server.co.uk"
Namespace(url='https://different.server.co.uk')
foo@bar:~$ unset SERVER_URL
foo@bar:~$ python mycli
Namespace(url=None)
usage: mycli [-h] [--url URL]
mycli: error: argument --url: Server URL must be provided via either the CLI or the environment variable SERVER_URL.
import argparse
import os
class EnvironDefault(str):
def __init__(self, url: str) -> None:
err_msg = (
'Server URL must be provided via either the CLI or the '
'environment variable SERVER_URL.')
if not url:
raise argparse.ArgumentTypeError(err_msg)
parser = argparse.ArgumentParser()
parser.add_argument(
'--url', type=EnvironDefault, default=os.environ.get('SERVER_URL'),
help='Server URL. Overrides environment variable SERVER_URL.')
print(parser.parse_args())
(Или динамически решить, требуется ли аргумент или нет; см. мой ответ ниже.)
Спасибо за ваши комментарии. Ваш ответ ниже направил меня прямо, так что спасибо за это. Тем не менее, мне любопытно ваше предложение переопределить ArgumentParser.parse_args
- предположительно, мне придется углубиться в объект ArgumentParser
, так как не похоже, что в parse_args
есть что-то, что я мог бы переопределить, чтобы передать значение по умолчанию типу первый.
Когда программа запускается, вы можете сразу проверить, доступен ли SERVER_URL
, и использовать этот факт, чтобы решить, требуется --url
или нет.
parser = argparse.ArgumentParser()
parser.add_argument(
'--url', required='SERVER_URL' not in os.environ, default=os.environ.get('SERVER_URL'),
help='Server URL. Overrides environment variable SERVER_URL.')
print(parser.parse_args())
Если SERVER_URL
не находится в среде, значение None
по умолчанию будет игнорироваться, поскольку --url
требуется. Если он находится в среде, то будет использоваться значение по умолчанию.
Обратите внимание, что значения по умолчанию не обрабатываются вашим типом. Если вы используете настраиваемый тип, его следует явно применить к значению по умолчанию. Например -
parser.add_argument('--value',
required='VALUE' not in os.environ,
type=int,
default=int(os.environ.get('VALUE')))
Это не то, что я использовал в прошлом для решения этой проблемы - просто подумал об этом сейчас - так что могут быть некоторые проблемы, о которых я еще не подумал.
Оба ваших предложения здесь работают, так что спасибо за это. Единственное, что следует отметить, это то, что по вашему второму предложению (явное применение типа к типу по умолчанию) исключение argparse.ArgumentTypeError
перехватывается за пределами argparse
, поэтому вы не получаете хорошо отформатированную ошибку при использовании.
Это правда, но на самом деле это «ваша» ответственность (а не argparse
) за правильность значения по умолчанию.
Согласен, просто упомянул об этом для процветания на случай, если другие наткнутся на это.
Значения аргументов по умолчанию назначаются непосредственно адресату, а не передаются сначала типу. Поскольку вы не узнаете, не предоставлен ли аргумент, до тех пор, пока не будет выполнен синтаксический анализ, вам придется либо переопределить
ArgumentParser.parse_args
, либо довольствоваться проверкой возвращаемого значенияparse_args
.