ValueError: No closing quotation в Python — причины и решение

Ошибка 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 в форматировании — он покажет строку в «сыром» виде, со всеми служебными символами. Так сразу видно и незакрытые кавычки, и лишние экранирующие слеши, и невидимые управляющие символы.

⚠️ Внимание: не исправляйте данные «вслепую» регулярными выражениями, автоматически дописывающими кавычки в конец строки. Такой подход может скрыть реальную порчу данных — например, обрезанное значение поля — и привести к тихим ошибкам в логике программы.
📊 Где вы столкнулись с ошибкой No closing quotation?
При вызове shlex.split()
При чтении CSV-файла
При разборе конфигурационного файла
При обработке пользовательского ввода

Способы исправления

Метод исправления зависит от того, контролируете ли вы источник данных. Если строки формируете вы сами — правильно экранируйте кавычки при сборке. Если данные приходят извне — добавьте валидацию и обработку исключений.

Для самостоятельно собираемых команд самый надёжный путь — вообще отказаться от ручной склейки строк. Передавайте аргументы списком в 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

Выполнено: 0 / 5

Профилактика ошибки в коде

Лучший способ не сталкиваться с 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-инъекций исчезает.