-
Блокнот

Оказывается, на 11 винде нельзя открыть блокнот (notepad.exe), если нет подключения к интернету. Причем как и все у микрософта, даже это ограничение сделано через задницу. По нажатию на иконку 20 секунд ничего не происходит, после чего вылезает плашка:
“Я не могу запустить это на этом устройстве. Чтобы запустить это, идите туда-то…”.
Зыс он зыс, ран зыс. Господи, какие же беспросветные уроды!
Честно говоря, не знаю, как можно пользоваться операционкой, где даже блокнот не запустишь без приключений. Яблочный софт в последнее время тоже не сахар, но хотя бы блокнот все еще открывается.
Кстати, недавно на работе была драма: люди обновили Edge и потеряли закладки, куки, историю. Взамен получили чистый браузер с приветствием: добро пожаловать в Edge!
Недаром Микрософт в последнее время называют Микрослопом. Все сходится.
-
Ссылки в терминале

Искренне интересно: кто эти люди, которые открывают ссылки из терминала не в родном браузере, а в неком плагине? Представьте себе: у человека установлен Хром, возможно, Фаерфокс, системные браузеры: Edge или Сафари. И человек такой: нет, это не подойдет, открою-ка я ссылку в ПЛАГИНЕ. И кто-то сидел, писал этот плагин, настраивал интеграцию, добавлял модальное окошко, чтобы при первом клике по ссылке спросить: тебе родной браузер или плагин?
Апофеоз подобного дурдома поражает. Этот ваш браузер из плагина, где он хранит куки? Как долго? Что с защитой от трекинга и блокировкой рекламы? Как он настраивается? Очевидно же, что никак. Зачем писать барахло и засорять когда-то хорошую программу? Неужели все проблемы уже решили? Но самое главное — тезис из начала заметки: что было в голове у тех, кто написал этот плагин? А также у тех, кто добавил эту функциональность в релиз.
-
Статьи в Телеграме
Долгое время в Телеграме нельзя было постить текст с картинками. То есть было можно, но со скрипом. К обычному тексту нельзя добавить картинку, а если это сделать, то сообщение становится типом “медиа”, и его текстовая часть сильно меньше. Премиум закрывает этот недостаток, но появляется другая беда: текст выравнивается по ширине картинки. Если картинка маленькая, текст превращается в мышиный хвост.
В результате народ постил текст и картинки отдельно, чтобы не страдало ни то, ни другое. Но получается два сообщения, и если хочешь поделиться ими, приходится пересылать пару. Словом, бред.
Со временем Телеграм стал полноценной медиаплощадкой; есть даже проекты, у которых единственный канал – месаджер. При публикации картинок каждый извращался как умел: выносил их в галереи, объединял в коллажи и так далее.
И вот в Телеграм завезли полноценный визивиг-редактор. Это когда картинки и текст чередуются, и нет смысла делить публикацию на текстовый блок и медиа. Также сделали заголовки, списки и редактор таблиц – этакий Ворд.
Что ж, на словах все прекрасно, а вот на деле… ради интереса я сверстал небольшую статью: три абзаца, а между ними картинки. Сбросил в сохраненные сообщения и проверил на ноутбуке и телефоне. Оказалось, что шрифт статьи на один-два пункта меньше, чем обычный текст. Получается, тот, кто читает с ноута, будет напрягать глаза. Проверил настройки: шрифт выкручен на максимум, отдельной опции для статей нет.

Также в статьях нет отступа между параграфами, приходиться лепить переносы строк.
На телефоне оба шрифта – обычной публикации и статьи – одинаковы, и это хорошо. Но блок статьи срезан слева на один сантиметр. Почему? Зачем его срезать? Для чего пустота слева? Там ни одной кнопки, просто щель шириной в сантиметр, и через нее сквозит фон.
На обоих устройствах клиенты последних версий.
Надеюсь, что в будущем шрифты подтянут. Пока что я не готов оформлять текст с картинками в виде статей: щурить глаза ради чередования картинок – так себе удовольствие.
-
Таблицы в маркдауне
Уж если вы взялись набирать таблицы в маркдауне, то делайте не так:
| id | title | comment | |---|---|---| | 1 | some title one | a long comment one | | 1001 | some | small commant | | 5551312 | - | foo |а вот так:
| id | title | comment | |---------|----------------|--------------------| | 1 | some title one | a long comment one | | 1001 | some | small commant | | 5551312 | - | foo |Как я эту кашу буду читать на ревью?
В емаксе это делается ОДНОЙ клавишей — TAB. Для других редакторов есть плагины. Вы и так все сгенерили нейронкой, неужели нельзя хоть чуть-чуть причесать?
-
О присваивании (2)
Небольшая добавка к заметке про
x += 5.В жизни каждого программиста был момент, когда учитель информатики писал на доске:
x = x + 1,после чего смотрел на класс – поймут или нет? Класс, разумеется, не понимал и смотрел на учителя: что он несет? В школьной математике переменные неизменяемы: если взялся решать квадратное уравнение или систему уравнений, x не может по ходу дела переобуться во что-то другое. Да, можно подать на вход разные иксы и получить разные игреки. Но одно дело входные данные, а другое – их мутация в полете.
Учитель начинал рассказывать про коробочку, из которой достали сто рублей, прибавили рубль и положили обратно в коробочку. Что ж, какое-то объяснение это давало. Чувствуя наше сомнение, учитель откровенно продавливал свою аргументацию: если написать в Паскале
x := x + 1, то икс увеличится. Видите?Спорить с этим было нельзя, но ясности не добавляло. Класс принимал объяснение как данность, но знаете что? Где-то глубоко во мне сидела мысль, что учитель сам не понимает, как это работает. Да, он знает, какой получится результат, но не понимает истинных причин. Просто потому, что когда что-то знаешь, объяснение простое.
Если бы меня попросили объяснить, как работает
x = x + 5, я бы ответил так.В языках высокого уровня мы работаем с переменными:
x,yи так далее. Этот код становится машинными командами, а в них никаких переменных нет. Лучше всего это видно на примере ассемблера. В ассемблере нет переменных – только адреса. Для удобства некоторым адресам можно сопоставить метку – логическое имя. Меткой может быть что угодно: переменная, точка входа в процедуру, место, куда нужно совершить условный переход и так далее.Выше я написал “переменная”, но это не совсем верно: в ассемблере нет переменных. Мы резервируем память и одновременно добавляем к ней метку. Если метку убрать, программа скомпилируется и будет работать, просто мы не сможем сослаться на эту память – или сможем, но косвенно, отталкиваясь от других меток.
Таким образом, метка – это адрес, просто именованный. Метка представляет собой число, и к нему можно что-то прибавить, чтобы сместиться в памяти. Если же взять метку в квадратные скобки, получим значение по указанному адресу. В этом и состоит двойственная природа метки: это адрес (число), однако можно либо взять ее значение по адресу, либо поместить значение по этому адресу.
Если предположить, что в ассемблерном коде есть метки
a,bиc, то выражениеa = b + cвыглядит так (пишу по памяти):mov eax [b] ; поместить в eax ЗНАЧЕНИЕ b add eax [c] ; добавить к eax ЗНАЧЕНИЕ c move a eax ; поместить ПО АДРЕСУ a значение eaxВидим, что в случае с “переменными”
bиcмы использовали оператор[], чтобы получить значение. А когда мы “присваиваем” результат вa, квадратные скобки не требуются – метка используется как адрес.Вот почему выражение
a = b + cследует читать так: в адресaзаносится выражение, полученное как… Важно, что не “aстановится” чем то, а именно “в адресa” заносится то-то и то-то. Большая разница.Язык Си предлагает указатели – считай, именованные адреса с той же семантикой. Мы вправе работать с ними как вздумается: использовать как числа для адресной арифметики или получать из них значения. Указатели – непростая тема, но по крайней мере здесь есть какой-то контроль. В выражении
x = x + 5этого контроля нет. Как я уже говорил, слева от присваивания стоит адрес, а если это что-то сложное вродеitems[get_index(foo)], оно вычисляется, чтобы получить этот адрес.Таким образом, переменная в Си — это тоже метка, и компилятор трактует ее по ситуации: иногда как значение, а иногда как адрес. Единственный язык, где адрес и значение не перемешиваются — это ассемблер. В нем
foo— это всегда адрес, а[foo]— значение по адресу.Как ни странно, присваивание неплохо ложится на объектную модель. Как мы выяснили, переменная – это тройка адрес-тип-значение. Ее операции: дай адрес, дай значение по адресу, помести значение по адресу. Можно представить переменную объектом с методами
getиset. В этом случае выражениеa = b + cмы бы записали так:a.set(b.get() + c.get())Или, если речь идет об
x = x + 1:x.set(x.get() + 1)Другое дело, что на тот момент, когда на доске впервые записано
x = x + 1, ученик еще не знает об объектах. Поэтому в ход идет коробочка, из которой достали сто рублей, добавили и положили обратно.Повторюсь: кому-то сказанное покажется банальным, но я считаю, о присваивании говорят мало – а тема очень интересная и сложная.
-
О присваивании (1)
Предположим, у нас следующий код:
x += 5Если спросить программиста, как он устроен, то скорее всего, ответ будет таким: это то же самое, что
x = x + 5Во-первых, это не верно, а во-вторых, такой ответ все равно ничего не объясняет.
Ошибка кроется в следующем. Если предположить, что
x += 5означаетx = x + 5, то из этого можно сделать вывод: любое выражение<whatever> += 5под капотом становится<whatever> = <whatever> + 5Представим теперь, что есть массив
m, и нужно увеличить его элемент на пять. Индекс этого элемента дает функцияfбез параметров. Таким образом, я запишу:m[f()] += 5Если следовать ошибочному предположению выше, это выражение становится таким:
m[f()] = m[f()] + 5Видим, что функция
f()вызывается дважды: сначала справа, чтобы посчитать значение, затем слева, чтобы присвоить его. Предположим теперь, функцияf()возвращает индекс случайно: в первый раз 2, а во второй — 7. Итоговое выражение станет таким:m[7] = m[2] + 5что совершенно неправильно. Так что тезис о том, что
x += 5равносильноx = x + 5— ошибочный.Если рассмотреть выражение
x = x + 5внимательней, станет ясно: у символаxв зависимости от положения слева или справа равенства разная семантика. Справа он используется для того, чтобы получить значение, а слева — адрес, по которому нужно его записать.В стандартах языков Си и Си++ для этого служат термины
lvalueиrvalue, что переводится как “лево-“ и “праводопустимые значения”. Так, на местеlvalue(слева от равенства) может быть любое выражение, от которого можно взять адрес. У правого выражения требования другие, и взятие адреса не требуется. Именно поэтому записьx = 5верная: отxможно взять адрес. По той же причине5 = xне пройдет компиляцию: от литерала 5 взять адрес нельзя.Даже если упростить выражение до предела и оставить
x = x, у обоих иксов будет разная семантика. Тот, что слева означает адрес, а справа — значение.Имея все это в виду, посмотрим, что происходит с выражением
m[f()] += 5. Его левая часть вычисляется, чтобы получить адрес, куда позже поместится результат. Если бы мы написали простоx += 5, то адрес x вычислять не надо — компилятор уже его знает. Однако дляm[f()]понадобится вычислитьf()и за счет адресной арифметики получить конечный адрес.Далее: значение из этого адреса помещается в отдельный регистр для вычислений, например
EAX. К этому регистру добавляется пятерка командойADD EAX 5(добавить что-то к ячейке памяти нельзя, только к регистру). После этого значение изEAXперемещается в тот адрес, что был вычислен в левом выражении. Функцияf(), таким образом, была вычислена один раз.Вывод таков, что даже если выражение записывается кратко, оно занимает несколько (порой много) машинных команд. Может, кому-то это очевидно, но не мешает лишний раз проговорить.
-
Глава 8. Функции на языке Python
Главы
- Введение в документы
- Базовые возможности JSON
- JSON в таблицах
- Индексирование JSON
- Ограничения в документах
- Язык путей JSONPath
- Отчеты, функции, расписание
- Функции на языке Python
- Версионирование и архивация документов
- Релевантный поиск
Содержание
- Главы
- Предварительные шаги
- Простые функции на Python
- Сторонние пакеты
- Работа с json(b). Трансформации
- Функции для работы с заявками
- Прочие сведения
Обсудим тему, которая, надеемся, разожжет в читателе интерес: как подключить к Postgres другие языки, например Python? Техника предлагает интересные возможности, особенно для работы с документами. Читатель узнает, как связать интерпретатор Python с базой данных и что можно сделать с его помощью. Считайте эту главу факультативом к предыдущей – дополнением, которое оказалось слишком длинным для параграфа и публикуется отдельно.
В прошлой главе мы познакомились с основами функций. В числе прочего мы упомянули, что функции можно писать на разных языках. По умолчанию это SQL, который не отличается от обычных запросов. Диалект plpgsql предлагает переменные, циклы, исключения и все то, что свойственно императивным языкам.
-
Пароли в Unix pass
Расскажу, как я храню пароли.
Когда-то давно я, как и все, пользовался 1Password. В те времена это была казуальная программка, легкая и незаменимая. Я купил ее долларов за 50, когда она была версии 4 или 5, и счастливо ей пользовался.
Как это часто бывает, программа прошла зенит своего удобства. Фирма, которая ей занималась, возомнила себя центром мира по безопасности. Штат раздули, программа все больше усложнялась, фирма вышла на корпоративный рынок… В какой-то момент 1Password развернул облачную инфраструктуру и ввел подписку. Вдобавок программу переписали на электрон и Node.js, так что еще одним процессом Хрома стало больше.
Я начал искать замену, нагуглил различные Bitwarden и аналоги. Программы хорошие, не спорю. Но вышло так, что мне, как Раскольникову, захотелось “страданье на душу принять”. Другими словами, я выбрал самый сложный способ хранить пароли – утилиту Unix pass. Вообще-то программа называется просто pass, но к ней добавляют Unix, чтобы было понятно – та самая.
Программа pass – это попытка передать философию ранней эпохи Unix. Pass написана даже не Си, а на шелле. Это скрипт на шесть экранов, который ничего не делает сам, а только командует следующими утилитами:
- gpg для (де)шифрования файлов;
- git для истории хранения и репликации;
- редактором для ввода данных;
- pinentry для безопасной передачи пароля от GPG-ключа.
Просто же, да?
Хранилище паролей выглядит как обычный гит-репозиторий с деревом папок и файлов. Никакой жесткой структуры, все определят пользователь. Единица хранения – файл. В файле может быть что угодно, однако со временем устоялись следующие соглашения:
- первая строка файла содержит пароль;
- другие строки хранят пары поле-значение в качестве метаданных.
Пример файла с паролем:
1dAfs@#sh_t335 email: ivan@grishaev.me username: igrishaev url: https://some.site/loginКоманда
pass path/to/fileдешифрует этот файл и выплюнет в консоль. Командаpath path/to/file -cпрочитает первую строку (пароль) и поместит в буфер обмена.Pass generateсоздает случайный пароль с разными параметрами (длина, алфавит),pass editоткрывает редактор, чтобы изменить существующий файл и так далее. Командаpass git ...совершает любое действие с репозиторием; чаще всего понадобитсяpushиpull. Коммиты на каждое действие программа создает сама.На маке вместо
pinentryпонадобитсяpinentry-mac– порт этой утилиты под яблочные устройства. Пропишите к ней путь в этом файлике:# ~/.gnupg/gpg-agent.conf pinentry-program /opt/homebrew/bin/pinentry-macЧтобы хранить все это добро, вам понадобится две пары ключей. Первая пара – публичный и закрытый ключи GPG. Вторая пара – SSH для репозитория. Дополнительно каждый приватный ключ должен быть зашифрован кодовым словом.
Все эти ключи следует распечатать на бумаге и хранить в разных местах: на работе, дома, у бабушки в деревне. Кодовые слова – в голове.
Поскольку unix pass – консольная утилита, для нее написана тьма графических оберток. Как и сам pass, они ужасны: поделки на C++, Tcl/Tk и прочее. В том числе есть плагины для Емакса и Вима. Попробовав пару оберток, я принял верное решение – предпочел консольную версию. Ее вполне достаточно.
Для Андроида написано несколько программ, для айфона – одна, которой я и пользуюсь. Обновляется она примерно раз в пять лет; топорная, местами странная, но работает. Первичная настройка тяжела: нужно перетащить GPG- и SSH-ключи на телефон, а они, как вы помните, занимают лист А4. В идеале вы копируете ключ на яблочном ноуте и телефон подхватывает буфер обмена — конечно, при соблюдении десятка условий.
Серьезный недостаток приложения в том, что оно не умеет решать конфликты Git. Если вы поправили файл одновременно на компе и телефоне, то при синхронизации программа скажет “конфликт”, и все – даже нет кнопки “принять своё” или “принять чужое”. Решается повторным скачиванием всего репозитория.
Вы, наверное, хотите знать, как я перенес пароли из 1Password в pass? В интернете полно Питон-скриптов, которые обходят экспорт 1Password и вставляют куда надо. Но сказано же: “страданье принять”. Я все сделал вручную. Примерно год я жил в режиме hit or miss: когда нужен был пароль, искал его в pass, а если не находил, переносил руками из 1Password. Со временем все нужное переехало в pass, а в старой системе остался хлам, который мне не нужен. У меня и сейчас установлен 1Password со старой базой, но я не открывал его уже много лет.
В этой статье я не буду описывать все детали установки. Предлагаю вам замечательный сайт-одностраничник, посвященный программе. Также есть достойная статья на Хабре “Знакомьтесь, pass”, где все подробно описано (но к некоторым вещам я пришел сам). Если у вас будут вопросы, задавайте: я отвечу и дополню заметку.
-
Экран маков
Я сменил несколько макбуков, и у всех одна и та же беда: на экране отпечатывается клавиатура. Поначалу это выглядит невинно: видны только уголки, но затем они собираются в квадратики. Если не принять мер, со временем отпечатывается контур тачпада.
Пишут, что отпечатки можно оттереть, но это не так. Часть следов пропадает, однако затем кнопки физически повреждают экран. Можно тереть следы до дыр – они никуда не денутся.



Я сменил три макбука, плюс параллельно пользуюсь корпоративным. Заметил, что чем раньше модель, тем устойчивей она к отпечаткам. Например, на ноуте 2014 года они появились чуть ли не через три года; на ноуте 2020 года – через два, на ноуте 2025 года – через несколько месяцев.
Почему это так, понять нетрудно. Ноутбуки стараются ужать, и расстояние между экраном и клавишами становится меньше. Я не измерял, но по ощущениям оно меньше миллиметра. Достаточно надавить пальцем, чтобы экран коснулся клавиш. Что уж говорить о переносе ноута в рюкзаке, когда на него давят другие вещи.
На корпоративный ноут я боюсь дышать и за все время носил его в рюкзаке раз десять. Однако и на нем уже появился нестираемый дефект. Ну а самая жесть – это нанотекстура.
Напомню, что в последний раз я брал ноут с нанотекстурой. Мне нравится матовый экран и тот факт, что видишь картинку вместо своего отражения. Однако такой экран очень нежный, любой тык пальцем заметен сильнее. Как следствие, он уязвим для отпечатков клавиш. Не прошло и нескольких месяцев, как на экране появились контуры кнопок и тачпада. Это именно физические повреждения, и никакие салфетки-тряпочки не помогут.
Насколько я понял, единственный способ избежать порчи экрана – это подкладывать что-то между ним и клавиатурой, когда ноут закрыт. Вопрос – что именно? В интернете советуют резиновую накладку или тряпочку, но на мой взгляд это опасно: то и другое толстые, а расстояние между экраном и кнопками – доля миллиметра.
Помните, когда в первый раз открываешь ноут, в нем постелена хрустящая тонкая бумага? Это и есть верное решение. Разумеется, та бумага сразу идет на помойку, и только потом понимаешь, что она не просто так. Поэтому берем обычный лист А4 и кладем в ноут. Делаю так уже какое-то время и заметил, что помогает: экран чище, следы перестали разрастаться.
Как правильно заметили на Reddit, по-хорошему надо подать на Эпл в суд. Пусть ноут будет толще на пару миллиметров, зато не испортится экран и не придется ходить с прокладкой. И я ни за что не поверю, что в самом Эпле не в курсе проблемы.
-
Just Learn SQL
Через третьи руки я получил интересную ссылку: What ORMs have taught me: just learn SQL. Статья интересная и в целом повторяет все то, о чем я говорил у себя в блоге. ORM – это игра, в которую невозможно выиграть. Она завязана на том противоречии, что в ORM продвинутые средства SQL недоступны. А если пользоваться только доступными средствами, то либо их не хватает, либо возникает лишний код в приложении.
Хотя все это очевидно, находятся те, кто не верят. Однако не обязательно верить мне или автору – можно обратиться к нейтральным источникам, которым не свойственны подтасовки.
Напомню, в Кложе есть два подхода для работы с SQL. Первый – библиотека Hugsql с очень простым принципом. Вы пишете сырой SQL, который позже становится функцией. В эту функцию передают подключение и параметры, а внутри выполняется тот SQL, что вы написали. Он может быть сколь угодно сложным и относиться к какому угодно диалекту: библиотека просто передает его драйверу и возвращает результат.
Второй вариант – библиотека HoneySQL для построения SQL из данных. Это еще не ORM, но уже шаг в данном направлении. Например, передаешь в библиотеку словарь с вектором векторов словарей и получается SQL. Болванку запроса легко строить по условиям или на базе каких-то других данных.
Так вот, интересен следующий факт. В библиотеке Hugsql последний коммит был два года назад и касался документации. Всего коммитов около 200. В отношении кода изменения были четыре-пять лет назад. Активных фаз у проекта всего две: в 2016 и 2022 годах, при этом число коммитов в это время измеряется десятками.

С HoneySQL ситуация другая: коммиты и релизы выходят постоянно. Всего коммитов около 1400. Шон Корфилд (автор проекта) уже давно развивает HoneySQL, при этом динамика проекта противоположная: коммиты и релизы выходят все чаще. В Слаке регулярно приходит народ и просит добавить ту или иную фичу какого-то диалекта: хитрую агрегацию этой базы, приведение типа другой базы и так далее.

Доходит до того, что один диалект ворует возможности другого. Например, я использовал недокументированные возможности HoneySQL, чтобы строить JSON Path в Postgres, например, что-то вроде
where doc @@ '$.path.to.attribute == "some-value" 'В одном из релизов все отвалилось. Оказалось, к Шону пришел какой-то тип и сказал: давай сделаем пути JSON для SnowflakeDB. В этой самой Snowflake другие кавычки и разделители, которые не дружат с Postgres. Мои решения превратились в тыкву, и пришлось экстренно исправлять.
К чему это все: выразить SQL на языке, отличном от SQL, в целом невозможно. Проект HoneySQL это подтверждает: даже не смотря на активную разработку, найдутся такие потребности, которые нельзя выразить списком словарей – а если и можно, то это долго и непонятно. У библиотеки Hugsql такой проблемы нет: пиши что считаешь нужным, она только передает запрос драйверу – то самое “just learn SQL” из заголовка статьи.
Конечно, для удобной работы нужны оба средства, однако важно понимать приоритеты. Чтобы выразить SQL в виде чего-то – ORM или DSL – нужно понимать SQL. При этом часто оказывается, что знания первого достаточно.