Когда проект на python долгое время живет без правил по формату строк, то в один прекрасный момент оказывается, что 90% кода используют одинарные кавычки, а 10% - двойные.
Добавление flake8-quotes с соответствующими правилами перестало пускать новый код с двойными кавычками дальше пул-реквеста, но начало требовать ручной правки формата в уже существующем коде, чего хотелось бы избежать.
Первой мыслью было задействовать black, но предлагаемый им формат предполагает исключительно использование двойных кавычек. В 2018 в github black был запрос Single quotes option формата строк, обсуждение было жарким, но закончилось оно лишь введением опции --skip-string-normalization
, позволявшей не трогать формат строк в проверяемом коде.
Самым простым в данной ситуации было просто согласиться с black, но команда и бóльшая часть кода были против. Пришлось искать другое решение, которое оказалось в модификации исходного кода black.
Сразу скажу, что все правила реализовать не получилось, так как в flake8-quotes можно задать разные правила для docstring и остальных многострочных строк, а логика black их, насколько удалось разобраться, не различает.
Тем не менее, с двойными кавычками в простых строках справиться удалось,
и вот что для этого потребовалось:
были скачаны исходники black 22.1.0 (это первая не-бета версия black и наш CI в данный момент использует эту версию в режиме проверки кода).
установлены необходимые ему библиотеки
Для Python 3.8:
click 8.1.3
colorama 0.4.4
mypy-extensions 0.4.3
pathspec 0.9.0
pip 21.3.1
platformdirs 2.5.2
tomli 2.0.1
typing_extensions 4.2.0
wheel 0.37.1
добавлен модуль
src/black/_black_version.py
c содержимымversion = '22.1.0'
Основные правки оказались сконцентрированы в методе normalize_string_quotes
модуля src/black/strings.py
:
Условие на входе метода
if value[:3] == '"""':
return s
elif value[:3] == "'''":
orig_quote = "'''"
new_quote = '"""'
elif value[0] == '"':
orig_quote = '"'
new_quote = "'"
else:
orig_quote = "'"
new_quote = '"'
заменил на
if value[:3] == '"""':
return s
elif value[:3] == "'''":
return s # оставляем как есть и строки с ''', и строки с """
elif value[0] == '"':
orig_quote = '"'
new_quote = "'"
else:
orig_quote = "'"
new_quote = '"'
а условие на выходе метода
if new_escape_count == orig_escape_count and orig_quote == '"':
return s # Prefer double quotes
заменил на
if new_escape_count == orig_escape_count and orig_quote == "'":
return s # Prefer single quotes
Через несколько минут работы модифицированного black проблема двойных кавычек в нашем проекте была решена (хорошо, остался еще этап ручного исправления """
в многострочных строках на '''
, но он тоже не занял много времени).
Из приятного. В коде нашлось несколько строк вида u'\xa0'
- видимо скопированных из советов для python 2 - и исправилось (удалило u в начале). Также исправились сами экранированные строки "\""
в '"'
Из неожиданного. Строки fr'\abc'
заменило на rf'\abc'
. Таких правок при необходимости легко было избежав закомментировав вызов метода normalize_string_prefix.
В итоге хотелось бы сказать, что пока все это делал, почти согласился с идеологией black, хотя доводы апологетов одинарных кавычек - они не требуют нажатия Shift для ввода, сам интерпретатор Python в консоли использует именно их, код выглядит прозрачней и тем самым более pythonic - тоже заслуживают внимания.
Что же касается opensource проектов, тут, я полагаю, все уже давно решено и black с его двойными кавычками победил.
Комментарии (34)
SergeiMinaev
19.06.2022 06:22-5Двойные кавычки в питоне, наверное, используют те, кто несколько раз нажимает пробел вместо одного таба :) Вспомнилось - https://www.youtube.com/watch?v=AXLoRpKnK8U
FibYar
19.06.2022 09:40+15Либо те, кто пришёл из других языков, где стандарт - двойные кавычки. Если бы питон позволял использовать только один вариант, проблемы бы не было.
nin-jin
19.06.2022 09:03+23Ерундой вы какой-то занимаетесь вместо работы. Что, дедлайн далеко, можно кавычки поперекрашивать?
rrrad
19.06.2022 09:27+4В 2018 в github black был запрос Single quotes option формата строк, обсуждение было жарким, но закончилось оно лишь введением опции
--skip-string-normalization
, позволявшей не трогать формат строк в проверяемом коде.По моему, единственный правильный способ борьбы с такими принципиальными проектами ("все должны использовать наши правила, либо не использовать наш форматтер, т.к. у нас нашлись серьёзные, с нашей точки зрения, причины именно для таких правил, а также этот выбор исключает споры) - создать форк (назвав его white :) ), который будет полной копией, за исключением правила с кавычками.
Xobotun
19.06.2022 09:50+6Я тут немного ради похоливарить и любопытство потешить. В джаве строка – всегда двойные кавычки, а единичный символьный литерал – одинарные. В груви двойные кавычки делают интерполируюмую GString, а одинарные – просто строку. В баше, кажется, тоже, внутри двойных кавычек
$
eval'уируется.Питоном же я почти не пользовался, да и то 2.7 веткой, когда она ещё жива была. Посему вопрос – а чем плохи двойные кавычки?
baldr
19.06.2022 10:10+3Пишу на Питоне больше 10 лет, но тоже удивился и зашел узнать ответ на этот вопрос.
Я использую и те и другие кавычки. Особого предпочтения нет, но чаще всего для имен ключей коллекций и коротких строк или символов использую одинарные. Для вывода какой-то строки, например, в лог я, скорее всего, возьму двойные, но это совсем не принципиально.
Судя по всему, это просто режет глаз кому-то сильно ортодоксальному. Вопрос похож на спор - какой размер табуляции использовать - 3 или 4 символа.
57an Автор
19.06.2022 10:26+2Вопрос не в том, что двойные кавычки чем-то плохи. Вопрос в том, как ввести правила по кавычкам на легаси-проекте, применив их сразу ко всему проекту и сохранив статистические предпочтения. Если бы такое же решил сделать в JavaScript, там eslint с опцией --fix сделал бы все автоматом, какой бы формат кавычек я бы не задал в правилах. Для Python такое решение - black - есть только для двойных кавычек.
Почему вообще нужны правила по кавычкам? Потому что если их не заводить, то по моему опыту периодически в пул-реквестах команды появляются случайные правки кавычек в старом коде. Ребята просто берут и правят все одиночные на двойные или наоборот в каком-то файле или куске файла, без видимых причин, потому что им показалось что так лучше. И с их точки зрения они вообще молодцы, что прибрались в коде, применили правило бой-скаута на деле. Чтобы не занимались этим вместо полезной работы ввожу правила и пусть за этим следит линтер.
netch80
19.06.2022 11:59+1Я пока что увидел для себя две причины выбирать один из вариантов:
1. Когда набираешь if __name__ == "__main__", с двойными кавычками меньше нажатий/отжатий Shift :)
2. В определённых областях применения — внутри целевых строк одиночные или двойные встречаются сильно чаще. Например, если вы рисуете подстановку строк-литералов в SQL, им требуются одинарные => вокруг надо делать двойные, а если имён объектов (таблиц, колонок) — двойные. (Да, я знаю, что по идеальной норме это всё давно сделано в некотором middleware. Это только для примера специфичной области.)
С другой стороны, тут скорее хочется чего-то в стиле C++ `R«abc(x»y«z»)abc"`, чтобы вообще иметь один универсальный независимый от контента формат квотинга. Ну или хотя бы `qq(x«y»z")`, как в Perl.
3. Редакторы, заточенные под текст, а не код, конвертят (сразу или при генерации выхода) "x" в “x”, причём плюя на локаль (для русского должно было быть „x“). С одинарными кавычками такого не происходит. Иногда это становится важным (сайты статей типа хабра, учебные материалы).tdemin
19.06.2022 20:59Когда набираешь if __name__ == "__main__", с двойными кавычками меньше нажатий/отжатий Shift :)
В том же VSCode на это вместе с расширением Python идет сниппет, позволяющий
main<Tab>
.R«abc(x»y«z»)abc"
Ох уж этот новый редактор хабра.
economist75
20.06.2022 09:58Очевидно же, что двойные кавычки плохи тем, что они распространены в других языках программирования (Python - "клей", часто использует инжекты другого кода). Ну и в современном прагматичном русском двойные кавычки (не елочки, они вредны во всех отношениях) - в названия компаний ООО "Ромашка", в литературе итд. А значит их труднее (чем в одинарных), внедрить и сохранить в строке python без экранирования, а значит правильнее всегда использовать одинарные кавычки (меньше печатать), сравните:
print('ООО "Ромашка"','далее','"исполнитель"')
print("ООО "\"Ромашка\"','далее','\"исполнитель\"') # на +5 больше нажатий клавиш (+10%)
Два варианта кавычек - еще одна киллерфича Питон, за какие его и любят. 10% роста производительности труда/печатания на дороге не валяются. Исключение - обязательное использование тройных двойных """ по PEP8 для docstring функций, это для автодокументирования, будущего парсинга кода для книгоиздателей итп. Ну и просто красиво. Их и набирать не надо - приличная IDE их после def() напечатает сама. Тройные одинарные тоже работают, но читаются хуже.
baldr
20.06.2022 10:05query = "select * from table where status = 'active'"
Вот вам примеры когда одинарные кавычки внутри текста лучше заключать во внешние двойные.
print("Please enter user's first name:")
amarao
19.06.2022 10:37+5Меня аргументы black'а убедили.
Сравните: '' + '' и " + "
Одно - пустая строка, второе - плюсик с пробелами.
Игры со шрифтами позволят увидеть разницу'' + '' и " + "
но в общем случае путаница может быть. Так что у них есть валидный аргумент, а т.к. всё остальное вкусовщина, то даже один слабый аргумент побеждает полное отсутствие аргументов.
baldr
19.06.2022 10:42+6Вы хотите сказать что бывают такие, кто пишет код не с моноширинным шрифтом? Вот такие на Python я даже не представляю что могут выжить без повреждения мозга.
amarao
19.06.2022 10:54+4Даже в моноширинном шрифте они всё равно вызывают неоднозначность, если находятся в середине сложного выражения.
Аргумент есть. А вот у сторонников одинарных кавычек аргументов кроме "мне так нравится" нет.
До аргументации black'а у меня не было оформленного мнения (кроме того, что любого человека из C/Rust бэкграунда корёжит от того, что путают &str и char), а тут появилось. Более того, оно появилось как готовое решение, которому можно просто подчиниться и забыть навсегда этот вопрос.
netch80
19.06.2022 12:05> А вот у сторонников одинарных кавычек аргументов кроме «мне так нравится» нет.
(повторюсь из соседнего комментария)
Редакторы, заточенные под текст, а не код, конвертят (сразу или при генерации выхода) "x" в “x”, причём плюя на локаль (для русского должно было быть „x“). С одинарными кавычками такого не происходит. Иногда это становится важным (сайты статей типа хабра, учебные материалы). С такими проще всё перевести на одинарные.amarao
19.06.2022 12:20Корёжит оба типа.
netch80
19.06.2022 13:11Кто этот злодей? Имя, сестра!
amarao
19.06.2022 13:13Самый типографичный сайт для текстов.
netch80
19.06.2022 13:18-1> medium.com
Мировое сборище жлобства за пейволлом? Не аргумент.amarao
19.06.2022 13:49У меня там блог без всякого жлобства, да и читаю я там много людей так же, как я и читал их до появления paywall'а.
... Вообще, при чём тут модель монетизации? Вы сказали "корраптят только двойные кавычки", я показал пример сайта, где коррапят оба вида.
netch80
19.06.2022 13:59>… Вообще, при чём тут модель монетизации?
При том, что его поведение таки нетипично. Ну да, «не происходит» надо было уточнить стандартным «в ~95% случаев», тут уж извините.
> У меня там блог без всякого жлобства
Ну так авторов там стараются холить и лелеять, бесспорно.amarao
19.06.2022 14:41Ну я сходу показал существование редакторов, которые корраптят оба вида.
Проверил в гугльдоках:
Интуиция мне говорит, что office 365 сделает то же самое.
Так что я отвергаю аргумент о том, что одинарные кавычки лучше переживают копипаст через типограферы.
rrrad
19.06.2022 11:02+3Даже если абстрагироваться от того, что пример высосан из пальца, само по себе это бред, не знаю ни одного ЯП, где было бы нормальным использование немоноширинного шрифта. А с Питоном - тут это особенно важно. При публикации кода на веб-ресурсах (в т.ч. на Хабре) принято тоже код моноширинным шрифтом оформлять.
amarao
19.06.2022 11:07+1Безусловно, но аргумент есть. Есть ли какой-то контр-аргумент в другую сторону?
alTus
19.06.2022 13:32+1В том треде репозитория black накидали очень много хороших аргументов, почему будет лучше, когда стиль кавычек можно выбрать самому. Хорошо, что мейнтейнер хотя бы добавил
skip-string-normalization
, а вообще мне показалось, что он отклонил запрос исходя из собственных субъективных предпочтений.Когда мы у себя внедряли black, я тоже рассматривал вариант с форком (но это сразу показалось сложно и лениво), поэтому в итоге пришли к варианту c pre-commit: т.е. в начале в .pre-commit-config.yaml указан black, потом isort, а потом
double-quote-string-fixer
(встроенный в pre-commit хук). Works like a charm.
zloddey
19.06.2022 15:28Использую
black
в своих питонячьих пет-проектах, форматирование включено в стандартный флоу. Один вызовmake
сначала приведёт стиль в норму, а потом запустит тесты. В общем-то, ради тестов я его обычно и запускаю, а форматирование становится приятным бонусом. Получается так: пишу код как хочу - с одинарными кавычками, длинными строками и т.п. - но на выходе автоматически получается "правильный" код. Ещё до попадания в коммит! Иногда система чуток сбоит (делаю коммит без проверки), но это не особо напрягает.Все автоматические утилиты имеют свою "цену использования". Чем дальше они от непосредственного момента написания кода, чем дольше работают, и чем больше ставят препонов, тем эта цена выше. Она выражается как в времени разработчика, которое уходит на переделку, так и в "затрачиваемых нервах". С этой точки зрения,
black
- практически идеальный инструмент, если использовать его правильно. А вотflake
я раньше использовал, но со временем выкинул - потому что он постоянно душнил к месту и не к месту, и при этом не предоставлял инструментов для автоматической чистки кода. Другими словами, цена использования оказалась для меня выше, чем приносимый профит.Массовое форматирование кода - неприятная штука, конечно. Но оно делается один раз на всю кодовую базу, после чего уже можно жить в новом, гораздо более приятном окружении. И не тратить кучу сил на впиливание и дальнейшую поддержку костылей по типу описанных в статье (уж извините).
didyk_ivan
20.06.2022 09:55+3Вопрос из чистого любопытства (возможно бестолковый) - а чем смесь разных типов кавычек плоха?
Rustified
20.06.2022 09:56+1На мой взгляд двойные кавычки лучше, потому что их легче заметить в тексте, сильнее в глаз бросаются.
Tanner
Меня тоже двойные кавычки подбешивают в некоторых проектах. Мне кажется, лучше всего базировать стайл-гайд на том, как работает
repr()
для строк: одиночные кавычки за исключением тех случаев, когда двойные кавычки помогают избежать лишнего ескейпинга. Но уж лучше кривой стайл-гайд, чем никакого.