Лучшая практика для передачи нескольких путей в качестве аргумента командной строки с учетом путей с пробелами

Фон

Модуль Python, который я пишу, имеет некоторые функции, доступные из интерфейса командной строки через python -m fun_module -p .... аргумент -p, --path принимает несколько путей. В настоящее время я определяю этот аргумент следующим образом:

parser = argparse.ArgumentParser(prog = "FunProg", add_help=True)
parser.add_argument(
    "-p",
    "--path",
    type=utils.dir_path,
    nargs = "+",
    help = "Start path(s) used to conduct the search. Multiple paths should be separated with spaces.",
)
args = parser.parse_args()

dir_path функция доступна через utils.py файл

import os

def dir_path(str_path):
    if os.path.isdir(str_path):
        return str_path
    else:
        raise NotADirectoryError(str_path)

main метод

def main(args):
    for iarg in args.path:
        print("Searching path: " + iarg)

Проблема

Обработка нескольких путей, разделенных пробелами и окруженных кавычками, не возвращает желаемых результатов.

Желаемый результат

python -m fun_module -p ~/Documents ~/Downloads/
Searching path: /Users/thisuser/Documents
Searching path: /Users/thisuser/Downloads/

python -m fun_module -p '~/Documents' '~/Downloads/'
Searching path: /Users/thisuser/Documents
Searching path: /Users/thisuser/Downloads/

python -m fun_module -p "~/Documents" "~/Downloads/"
Searching path: /Users/thisuser/Documents
Searching path: /Users/thisuser/Downloads/

Ошибка:

python -m fun_module -p '~/Documents' '~/Downloads/'                          
Traceback (most recent call last):
  File "/Users/thisuser/.pyenv/versions/3.9.0/lib/python3.9/runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/Users/thisuser/.pyenv/versions/3.9.0/lib/python3.9/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/Users/thisuser/Dev/Python/FileLister/filelister/__main__.py", line 25, in <module>
    args = parser.parse_args()
  File "/Users/thisuser/.pyenv/versions/3.9.0/lib/python3.9/argparse.py", line 1818, in parse_args
    args, argv = self.parse_known_args(args, namespace)
  File "/Users/thisuser/.pyenv/versions/3.9.0/lib/python3.9/argparse.py", line 1851, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
  File "/Users/thisuser/.pyenv/versions/3.9.0/lib/python3.9/argparse.py", line 2060, in _parse_known_args
    start_index = consume_optional(start_index)
  File "/Users/thisuser/.pyenv/versions/3.9.0/lib/python3.9/argparse.py", line 2000, in consume_optional
    take_action(action, args, option_string)
  File "/Users/thisuser/.pyenv/versions/3.9.0/lib/python3.9/argparse.py", line 1912, in take_action
    argument_values = self._get_values(action, argument_strings)
  File "/Users/thisuser/.pyenv/versions/3.9.0/lib/python3.9/argparse.py", line 2461, in _get_values
    value = [self._get_value(action, v) for v in arg_strings]
  File "/Users/thisuser/.pyenv/versions/3.9.0/lib/python3.9/argparse.py", line 2461, in <listcomp>
    value = [self._get_value(action, v) for v in arg_strings]
  File "/Users/thisuser/.pyenv/versions/3.9.0/lib/python3.9/argparse.py", line 2476, in _get_value
    result = type_func(arg_string)
  File "/Users/thisuser/Dev/Python/FileLister/filelister/utils.py", line 7, in dir_path
    raise NotADirectoryError(str_path)
NotADirectoryError: ~/Documents

Вы пытались обернуть строку пути в os.path.normpath(path_str). Посмотрите на эту функцию вместе с os.path.abspath и os.path.relpath

oskros 12.12.2020 18:35

Помогает ли этот пост?

Thymen 12.12.2020 18:41
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
865
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема состояла из двух частей:

  1. Как указано в этом посте, вам нужно использовать os.path.expanduser при использовании ~.
  2. Когда ваш ввод содержит кавычки (' или "), os.path.expanduser не будет правильно обрабатывать ввод.

Чтобы решить проблемы, сначала замените кавычки пустыми строками. Используя python 3.9, это можно сделать с помощью:

for quote in ['"', "'"]:
    str_path = str_path.removeprefix(quote).removesuffix(quote)

в противном случае вам придется вручную проверять и удалять:

for quote in ['"', "'"]:
   if str_path.startswith(quote):
      str_path = str_path[1:-1]

И тогда вы должны проверить начало тильды:

if str_path.startswith('~'):
    str_path = os.path.expanduser(str_path)

Полный код:

def dir_path(str_path: str):
    for quote in ['"', "'"]:
        if str_path.startswith(quote):
            str_path = str_path[1:-1]
    if str_path.startswith('~'):
        str_path = os.path.expanduser(str_path)
    if os.path.isdir(str_path):
        return str_path
    raise NotADirectoryError(str_path)

И тестирование с любым из следующих входных данных приведет к полному пути:

python -m fun_module -p '~/Documents' '~/Downloads'
python -m fun_module -p "~/Documents" "~/Downloads"
python -m fun_module -p ~/Documents ~/Downloads

Другие вопросы по теме