Python: парсим аргументы командной строки

Категории: HowTo

Сегодня разберемся с сабпарсерами объекта argparse.ArgumentParser, которые передаются через параметр parents.

В общем случае при попытке распарсить аргументы командной строки мы создаем объект и начинаем методично добавлять к нему все известные нам параметры.

Иногда можно встретить очень большие листинги. Но существует механизм, который позволяет разбивать парсеры на более мелкие составляющие и сцеплять их между собой для более простого понимания (кстати, очень полезно при создании системы плагинов).

При этом мы можем управлять тем, какие аргументы будут парсится.

Для этого в параметры parents можно передать те объекты, от которых надо унаследоваться.

Рассмотрим на примерах.

В первом случае создается ArgumentParser с одним аргументом. И он сразу же разбирает командную строку на известные параметры и хвост при помощи parse_known_args.

В зависимости от значения -l мы либо продолжим работу, либо остановимся.

import argparse  
  
parser = argparse.ArgumentParser(add_help=False)  
parser.add_argument('-l', '--list', help="List something", action='store_true')  
args, remaining = parser.parse_known_args()  
  
if args.list:  
   print('List')  
   exit(0)  
  
parser = argparse.ArgumentParser(  
   description="Chained parsers",  
   formatter_class=argparse.RawDescriptionHelpFormatter,  
   parents=[parser])  
  
parser.add_argument('-t', '--test', help="Test", action='store_true')  
other_args = parser.parse_args(remaining)  
  
print('Args: {}'.format(other_args))

Второй пример чуть более сложный.

Допустим, что код обрабатывает видеофайлы и аудиофайлы. В зависимости от этого у него меняется поведение атрибута -r.

import argparse  
import enum  
  
class Formats(enum.Enum):  
   VIDEO='video'  
   AUDIO='audio'  
  
   def __str__(self):  
       return self.value  
  
def parser_common():  
   parser = argparse.ArgumentParser(add_help=False)  
   parser.add_argument(  
       '-f', '--format', help="Specify format", type=Formats, choices=list(Formats)  
   )  
   return parser  
  
def parser_video(parent_parser: argparse.ArgumentParser) -> argparse.ArgumentParser:  
   parser = argparse.ArgumentParser(  
       description="Video parser",  
       formatter_class=argparse.RawDescriptionHelpFormatter,  
       parents=[parent_parser])  
   parser.add_argument(  
       '-r',  
       '--resolution',  
       help="Resolution",  
       type=int,  
       nargs=2,  
       metavar=('WIDTH', 'HEIGHT'),  
       required=True,  
   )  
   return parser  
  
def parser_audio(parent_parser: argparse.ArgumentParser) -> argparse.ArgumentParser:  
   parser = argparse.ArgumentParser(  
       description="Audio parser",  
       formatter_class=argparse.RawDescriptionHelpFormatter,  
       parents=[parent_parser])  
   parser.add_argument(  
       '-r',  
       '--resolution',  
       help="Audit resolution",  
       type=int,  
       required=True,  
   )  
   return parser  
  
def main():  
   parser = parser_common()  
   args, remaining = parser.parse_known_args()  
   if args.format == Formats.AUDIO:  
       parser = parser_audio(parser)  
   elif args.format == Formats.VIDEO:  
       parser = parser_video(parser)  
   else:  
       parser.print_help()  
       exit(1)  
  
   other_args = parser.parse_args(remaining)  
   print(2)  
   print('Args: {}'.format(other_args))  
  
if __name__ == '__main__':  
   main()

Запустите и посмотрите, как ведёт себя данный код.

$ python argsparser.py
usage: argsparser.py [-f {video,audio}]

options:
  -f {video,audio}, --format {video,audio}
                        Specify format
$ python argsparser.py -f audio
usage: argsparser.py [-h] [-f {video,audio}] -r RESOLUTION
argsparser.py: error: the following arguments are required: -r/--resolution
$ python argsparser.py -f video
usage: argsparser.py [-h] [-f {video,audio}] -r WIDTH HEIGHT
argsparser.py: error: the following arguments are required: -r/--resolution
$ python argsparser.py -f video -h
usage: argsparser.py [-h] [-f {video,audio}] -r WIDTH HEIGHT

Video parser

options:
  -h, --help            show this help message and exit
  -f {video,audio}, --format {video,audio}
                        Specify format
  -r WIDTH HEIGHT, --resolution WIDTH HEIGHT
                        Resolution
$ python argsparser.py -f audio -h
usage: argsparser.py [-h] [-f {video,audio}] -r RESOLUTION

Audio parser

options:
  -h, --help            show this help message and exit
  -f {video,audio}, --format {video,audio}
                        Specify format
  -r RESOLUTION, --resolution RESOLUTION
                        Audit resolution

SDDM: Переключаем раскладку автоматически при блокировании экрана

Категории: HowTo

Продолжаем разбираться с окном логина (sddm, gdm и прочие *dm).

В предудущей части мы научились переключать раскладку из консоли минуя кнопки и мышки.

А теперь мы научимся менять раскладку автоматически при блокировании экрана.

Зачем это нужно? Чаще всего пароль у нас на латинице. А заблокировать экран можно при любой включенной раскладке. Это приводит к тому, что на экране логина эту самую раскладку на до менать. Неудобно!

1. Определяем, когда система была заблокирована

#!/usr/bin/env bash

dbus-monitor --session "type='signal',interface='org.freedesktop.ScreenSaver'" |
  while read x; do
    case "$x" in 
      *"boolean true"*) echo SCREEN_LOCKED;;
      *"boolean false"*) echo SCREEN_UNLOCKED;;  
    esac
  done

Если запустить скрипт, заблокировать, а потом разблокировать экран, то мы увидим его работу.

SCREEN_LOCKED
SCREEN_LOCKED
SCREEN_UNLOCKED
SCREEN_UNLOCKED

Два сообщения из-за особенностей интерфейса. Можно привязываться к другому интерфейсу, но в данном случае это некритично.

2. Учимся запускать скрипт фоном через systemd

Делаем скрипт /.local/bin/set-layout-on-lock. Не забывайте поставить на него `+x .

#!/usr/bin/env bash

dbus-monitor --session "type='signal',interface='org.freedesktop.ScreenSaver'" |
  while read x; do
    case "$x" in 
      *"boolean true"*) qdbus org.kde.keyboard /Layouts org.kde.KeyboardLayouts.setLayout 0;;
      *"boolean false"*) echo SCREEN_UNLOCKED;;  
    esac
  done

Создаём каталог для сервисов systemd.

$ mkdir -p ${HOME}/.config/systemd/user

Пишем сервис в ${HOME}/.config/systemd/user/set-kbd-layout-on-lock.service.

[Unit]
Description=Set custom locale on screen lock

[Service]
Restart=always
ExecStart=%h/.local/bin/set-layout-on-lock

[Install]
WantedBy=graphical-session.target
  • %h - означает директорию текущего пользователя.
  • graphical-session.target - означает, что сервис будет запускаться только с графическим интерфейсом (не нужен он нам в консоли).

3. Включаем

$ systemctl --user enable --now set-kbd-layout-on-lock

Сервис будет включен и тут же запущен.

Посмотреть состояние можно командой status.

$ systemctl --user status set-kbd-layout-on-lock

Литература

KDE: Переключаем раскладку из консоли

Категории: HowTo

Очень полезной штукой является возможность переключить раскладку из консоли.

Зачем? Например, при блокировании сеанса надо сделать так, чтобы раскладка стала английской.

Так как дистрибутивы очень сильно видоизменились и стали поставляться с wayland и производными systemd (keyboad, logind, …), то переключать стандартными линуксовыми средствами вроде setxkbmap, xkb-switch и прочими способами не получиться.

Теперь раскладка в дистрибутивах переключается при помощи dbus и сигналов.

Для kde это делается вызовом метода org.kde.KeyboardLayouts.setLayout в сервисе org.kde.keyboard.

$ qdbus org.kde.keyboard /Layouts org.kde.KeyboardLayouts.setLayout 1

Layout switch

Последний аргумент - это номер раскладки в списке.

Посмотреть список раскладок можно через другой метод.

$ qdbus --literal org.kde.keyboard /Layouts org.kde.KeyboardLayouts.getLayoutsList
[Argument: a(sss) {[Argument: (sss) "us", "", "Английская (США)"], [Argument: (sss) "ru", "", "Русская"]}]

Обязательный аргумент в нашем случае - это --literal. Без него команда просто выведет сообщение о том, что не может отобразить нестандарный тип данных.

$ qdbus org.kde.keyboard /Layouts org.kde.KeyboardLayouts.getLayoutsList
qdbus: I don't know how to display an argument of type 'a(sss)', run with --literal.

Linux: Система засыпает при заблокированном сеансе

Категории: HowTo

Gnome и Kde страдают тем, что на экране логина за электропитание отвечает не утилита управления электропитанием, а logind из состава systemd и настроить поведение компьютера в этом режиме в панелях управления нельзя.

Проблема наблюдается как в gdm, так и sddm.

Проявляется в следующих случаях:

  • Включили машину, но не вошли в систему -> компьютер ушел в спячку
  • Включили ноутбук с внешним монитором, поработали и заблокировали сеанс -> система ушла в спячку если закрыть крышку

Есть даже много отчетов об ошибках и информация от разработчиков о том, что это сделано “ради прохождения сертификации энергопотребления”.

Чтобы избежать засыпания системы после включения нужно сконфигурировать gdm отключив ему спячку (sddm, lxdm и т.п. работают нормально).

sudo -u gdm dbus-run-session gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-timeout 0

Чтобы избежать проблемы засыпания ноутбука при закрытой крышке нужно дополнительно прописать настройки в /etc/systemd/login.conf.d/00-lid.conf или же в /etc/systemd/login.conf.

[Login]  
HandleLidSwitchExternalPower=ignore  
HandleLidSwitchDocked=ignore

IDEA: Не работает превью для Markdown

Категории: HowTo

There are no available preview providers

Есть вероятность, что возможность смотреть превью markdown-файлов в соответствующем плагине отсутствует.

Скорее всего проблема в jdk и jfx, которые вы используете. Чаще всего используются те, что идут бандлом с самое ide.

Нужно поменять:

  • идем в быстрый поиск (двойное нажатие shift)
  • вбиваем choose boot java runtime for ide
  • выбираем другую версию jdk c jcef
  • плагин должен заработать корректно
  • если не случилось - повторяем (можно использовать системный jre)

Choose JRE

Choose new version

Done