Ошибка ValueError: No closing quotation возникает в Python, когда парсер встречает открывающую кавычку, но не находит для неё закрывающую пару — чаще всего это происходит при вызове shlex.split() на строке с незакрытой кавычкой или при разборе CSV-данных модулем csv в строгом режиме. Типичный пример: строка вида 'command "argument' передаётся в shlex.split(), и интерпретатор прерывает выполнение, потому что двойная кавычка после слова argument так и не была закрыта.
Ошибка относится к разряду легко исправимых: она почти всегда указывает на конкретное место в данных, где нарушен баланс кавычек. Однако найти это место в большом файле или в динамически формируемой строке бывает непросто. Ниже разберём, где именно возникает исключение, как его локализовать и какие способы исправления существуют для разных сценариев.
Где именно возникает ошибка
Чаще всего No closing quotation выбрасывает модуль shlex — стандартный лексический анализатор оболочки. Функция shlex.split() разбирает строку по правилам shell, где кавычки группируют аргументы с пробелами. Если кавычка открыта и не закрыта, лексер доходит до конца строки и сообщает об ошибке.
Вторая частая точка возникновения — модуль csv при чтении файлов, где поля обёрнуты в кавычки. Если в данных встречается «битая» строка с незакрытой кавычкой, парсер в строгом режиме (strict=True) выбрасывает исключение. Похожая ситуация возможна и при ручном разборе конфигурационных файлов, где значения параметров заключены в кавычки.
Быстро проверить, виноват ли shlex, можно так:
import shlex
try:
shlex.split('run --name "My App')
except ValueError as e:
print(e) # No closing quotation
Основные причины появления
Корень проблемы всегда один — нарушенный баланс кавычек, — но попасть в такую ситуацию можно по-разному. Понимание конкретного сценария помогает выбрать правильное исправление, а не просто «залатать» строку.
- 🔤 Незакрытая кавычка в исходных данных — строка пришла из файла, API или пользовательского ввода уже повреждённой.
- ✂️ Обрезанная строка — данные усечены по лимиту длины (буфер, лимит поля в БД, обрезка лога), и закрывающая кавычка потеряна.
- 🔀 Смешение типов кавычек — строка открыта двойной кавычкой, а закрыта одинарной или наоборот.
- 💬 Кавычка внутри текста — апостроф или кавычка в естественном тексте (например,
it's "quoted) воспринимается парсером как начало квотирования. - 📁 Пути Windows с обратным слешем — последовательность
\"в конце пути экранирует закрывающую кавычку, и она «исчезает» для парсера.
Как локализовать проблемное место
Прежде чем чинить, нужно понять, какая именно строка ломает разбор. Если данные читаются из файла построчно, оберните вызов парсера в try/except и выведите номер строки и её содержимое:
import shlex
with open("commands.txt", encoding="utf-8") as f:
for num, line in enumerate(f, 1):
try:
args = shlex.split(line)
except ValueError:
print(f"Строка {num}: {line!r}")
Обратите внимание на !r в форматировании — он покажет строку в «сыром» виде, со всеми служебными символами. Так сразу видно и незакрытые кавычки, и лишние экранирующие слеши, и невидимые управляющие символы.
⚠️ Внимание: не исправляйте данные «вслепую» регулярными выражениями, автоматически дописывающими кавычки в конец строки. Такой подход может скрыть реальную порчу данных — например, обрезанное значение поля — и привести к тихим ошибкам в логике программы.
Способы исправления
Метод исправления зависит от того, контролируете ли вы источник данных. Если строки формируете вы сами — правильно экранируйте кавычки при сборке. Если данные приходят извне — добавьте валидацию и обработку исключений.
Для самостоятельно собираемых команд самый надёжный путь — вообще отказаться от ручной склейки строк. Передавайте аргументы списком в subprocess.run(), тогда кавычки и экранирование не понадобятся вовсе:
import subprocess
Вместо shell-строки с кавычками:
subprocess.run(["mytool", "--name", "My App", "--path", r"C:\My Files"])
Если без shlex.split() не обойтись, а входные данные могут быть «грязными», настройте лексер мягче. У объекта shlex.shlex можно отключить обработку кавычек или задать свой набор квотирующих символов — тогда незакрытая кавычка перестанет вызывать исключение, хотя и изменится логика разбора.
Работа с CSV-файлами
При чтении CSV модуль csv трактует кавычку как начало поля, внутри которого допустимы запятые и переводы строк. Незакрытая кавычка заставляет парсер «проглатывать» последующие строки файла как часть одного поля или выбрасывать ошибку в строгом режиме.
Проверьте файл на типичные дефекты: поля, где кавычка стоит в середине текста без экранирования, строки с нечётным количеством кавычек, обрезанные последние строки. В стандарте CSV кавычка внутри квотированного поля должна удваиваться ("") — если источник данных этого не делает, файл формально некорректен.
| Сценарий | Признак | Способ решения |
|---|---|---|
| shlex.split() на своих строках | Ошибка при разборе команды | Перейти на список аргументов в subprocess |
| shlex.split() на внешних данных | Ошибка на конкретных строках | Валидация + try/except с логированием |
| Чтение CSV | Сбой на «битой» строке файла | Исправить источник или обработать строку вручную |
| Пути Windows в кавычках | Слеш перед закрывающей кавычкой | Убрать или удвоить завершающий слеш |
| Апострофы в тексте | Ошибка на словах вроде it's | Отключить квотирование или нормализовать текст |
☑️ Диагностика ошибки No closing quotation
Профилактика ошибки в коде
Лучший способ не сталкиваться с No closing quotation — проектировать код так, чтобы ручной разбор кавычек вообще не требовался. Для запуска внешних программ используйте списки аргументов, для CSV — проверенные библиотеки записи, которые корректно экранируют поля, для конфигураций — форматы вроде JSON, YAML или TOML с готовыми парсерами.
Если входные данные неизбежно приходят в виде «сырых» строк, добавьте на входе валидацию: подсчёт кавычек, проверку длины, отклонение явно повреждённых значений с понятным сообщением. Это дешевле, чем разбирать падения парсера в продакшене.
Почему shlex ведёт себя иначе на Windows
В POSIX-режиме shlex обрабатывает обратный слеш как экранирующий символ, поэтому пути Windows вида C:\dir\" ломают разбор. Передайте posix=False в shlex.split() — тогда слеши сохранятся, но изменится и обработка кавычек: они останутся в результирующих токенах.
⚠️ Внимание: параметрposix=Falseвshlex.split()меняет семантику разбора — кавычки перестают удаляться из токенов. Не применяйте его как «универсальное лекарство» без проверки того, как downstream-код обрабатывает результат.
Частые вопросы
Можно ли просто подавить ошибку через try/except?
Технически да, но это скроет повреждённые данные. Правильнее перехватывать исключение, логировать проблемную строку и решать, как её обрабатывать: пропускать, чинить или отклонять весь набор данных.
Почему ошибка возникает на путях Windows?
Обратный слеш перед закрывающей кавычкой экранирует её, и парсер считает квотирование незавершённым. Уберите завершающий слеш из пути, удвойте его или передавайте путь отдельным элементом списка аргументов без кавычек.
Как прочитать CSV с незакрытыми кавычками?
Лучший вариант — исправить сам файл или источник, который его генерирует. Если это невозможно, попробуйте читать файл с параметром quoting=csv.QUOTE_NONE и разбирать поля вручную, либо предобработать строки до передачи в парсер.
shlex.split() падает на тексте с апострофом — что делать?
Апостроф воспринимается как одинарная кавычка. Если вы разбираете обычный текст, а не shell-команду, shlex — неподходящий инструмент; используйте str.split() или регулярные выражения.
Чем заменить shlex.split() для надёжного запуска команд?
Ничем — заменять нужно подход. Передавайте в subprocess.run() список аргументов вместо строки: тогда разбор кавычек не нужен вовсе, а риск ошибок и shell-инъекций исчезает.