diff --git a/jc/lib.py b/jc/lib.py index cb164352..bbc715b0 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -94,6 +94,7 @@ parsers = [ 'ufw', 'ufw-appinfo', 'uname', + 'update-alt-q', 'upower', 'uptime', 'vmstat', diff --git a/jc/parsers/update_alt_q.py b/jc/parsers/update_alt_q.py new file mode 100644 index 00000000..bbfcbf64 --- /dev/null +++ b/jc/parsers/update_alt_q.py @@ -0,0 +1,261 @@ +"""jc - JSON Convert `update-alternatives --query` command output parser + +Usage (cli): + + $ update-alternatives --query | jc --update-alt-q + + or + + $ jc update-alternatives --query + +Usage (module): + + import jc + result = jc.parse('update_alt_q', + update_alternatives_query_command_output) + +Schema: + + { + "name": string, + "link": string, + "slaves": [ + { + "name": string, + "path": string + } + ], + "status": string, + "best": string, + "value": string, # (null if 'none') + "alternatives": [ + { + "name": string, + "priority": integer, + "slaves": [ + { + "name": string, + "path": string + } + ] + } + ] + } + +Examples: + + $ update-alternatives --query editor | jc --update-alt-q -p + { + "name": "editor", + "link": "/usr/bin/editor", + "slaves": [ + { + "name": "editor.1.gz", + "path": "/usr/share/man/man1/editor.1.gz" + }, + { + "name": "editor.da.1.gz", + "path": "/usr/share/man/da/man1/editor.1.gz" + } + ], + "status": "auto", + "best": "/bin/nano", + "value": "/bin/nano", + "alternatives": [ + { + "name": "/bin/ed", + "priority": -100, + "slaves": [ + { + "name": "editor.1.gz", + "path": "/usr/share/man/man1/ed.1.gz" + } + ] + }, + { + "name": "/bin/nano", + "priority": 40, + "slaves": [ + { + "name": "editor.1.gz", + "path": "/usr/share/man/man1/nano.1.gz" + } + ] + } + ] + } + + $ update-alternatives --query | jc --update-alt-q -p -r + { + "name": "editor", + "link": "/usr/bin/editor", + "slaves": [ + { + "name": "editor.1.gz", + "path": "/usr/share/man/man1/editor.1.gz" + }, + { + "name": "editor.da.1.gz", + "path": "/usr/share/man/da/man1/editor.1.gz" + } + ], + "status": "auto", + "best": "/bin/nano", + "value": "/bin/nano", + "alternatives": [ + { + "name": "/bin/ed", + "priority": "-100", + "slaves": [ + { + "name": "editor.1.gz", + "path": "/usr/share/man/man1/ed.1.gz" + } + ] + }, + { + "name": "/bin/nano", + "priority": "40", + "slaves": [ + { + "name": "editor.1.gz", + "path": "/usr/share/man/man1/nano.1.gz" + } + ] + } + ] + } +""" +from typing import List, Dict +import jc.utils + + +class info(): + """Provides parser metadata (version, author, etc.)""" + version = '1.0' + description = '`update-alternatives --query` command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + compatible = ['linux'] + magic_commands = ['update-alternatives --query'] + + +__version__ = info.version + + +def _process(proc_data: Dict) -> Dict: + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (Dictionary) raw structured data to process + + Returns: + + Dictionary. Structured to conform to the schema. + """ + int_list = ['priority'] + + if 'value' in proc_data: + if proc_data['value'] == 'none': + proc_data['value'] = None + + if 'alternatives' in proc_data: + for index, alt in enumerate(proc_data['alternatives']): + for key in alt: + if key in int_list: + proc_data['alternatives'][index][key] = jc.utils.convert_to_int(proc_data['alternatives'][index][key]) + + return proc_data + + +def parse( + data: str, + raw: bool = False, + quiet: bool = False +) -> Dict: + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + + Returns: + + Dictionary. Raw or processed structured data. + """ + jc.utils.compatibility(__name__, info.compatible, quiet) + jc.utils.input_type_check(data) + + raw_output: Dict = {} + slaves: List = [] + alt_obj: Dict = {} + + if jc.utils.has_data(data): + + for line in filter(None, data.splitlines()): + line_list = line.split(maxsplit=1) + + if line.startswith('Name: '): + raw_output['name'] = line_list[1] + continue + + if line.startswith('Link: '): + raw_output['link'] = line_list[1] + continue + + if line.startswith('Slaves:'): + continue + + if line.startswith(' '): + s_name = line_list[0].strip() + s_path = line_list[1] + slaves.append( + { + "name": s_name, + "path": s_path + } + ) + continue + + if line.startswith('Status: '): + if slaves: + raw_output['slaves'] = slaves + slaves = [] + raw_output['status'] = line_list[1] + continue + + if line.startswith('Best: '): + raw_output['best'] = line_list[1] + continue + + if line.startswith('Value: '): + raw_output['value'] = line_list[1] + continue + + if line.startswith('Alternative: '): + if not 'alternatives' in raw_output: + raw_output['alternatives'] = [] + + if slaves: + alt_obj['slaves'] = slaves + raw_output['alternatives'].append(alt_obj) + slaves = [] + + alt_obj = {"name": line_list[1]} + continue + + if line.startswith('Priority: '): + alt_obj['priority'] = line_list[1] + continue + + if alt_obj: + if slaves: + alt_obj['slaves'] = slaves + raw_output['alternatives'].append(alt_obj) + + return raw_output if raw else _process(raw_output) diff --git a/tests/fixtures/generic/update-alternatives-query.out b/tests/fixtures/generic/update-alternatives-query.out new file mode 100644 index 00000000..56c31b60 --- /dev/null +++ b/tests/fixtures/generic/update-alternatives-query.out @@ -0,0 +1,48 @@ +Name: editor +Link: /usr/bin/editor +Slaves: + editor.1.gz /usr/share/man/man1/editor.1.gz + editor.da.1.gz /usr/share/man/da/man1/editor.1.gz + editor.de.1.gz /usr/share/man/de/man1/editor.1.gz + editor.fr.1.gz /usr/share/man/fr/man1/editor.1.gz + editor.it.1.gz /usr/share/man/it/man1/editor.1.gz + editor.ja.1.gz /usr/share/man/ja/man1/editor.1.gz + editor.pl.1.gz /usr/share/man/pl/man1/editor.1.gz + editor.ru.1.gz /usr/share/man/ru/man1/editor.1.gz +Status: auto +Best: /bin/nano +Value: /bin/nano + +Alternative: /bin/ed +Priority: -100 +Slaves: + editor.1.gz /usr/share/man/man1/ed.1.gz + +Alternative: /bin/nano +Priority: 40 +Slaves: + editor.1.gz /usr/share/man/man1/nano.1.gz + +Alternative: /usr/bin/vim.basic +Priority: 30 +Slaves: + editor.1.gz /usr/share/man/man1/vim.1.gz + editor.da.1.gz /usr/share/man/da/man1/vim.1.gz + editor.de.1.gz /usr/share/man/de/man1/vim.1.gz + editor.fr.1.gz /usr/share/man/fr/man1/vim.1.gz + editor.it.1.gz /usr/share/man/it/man1/vim.1.gz + editor.ja.1.gz /usr/share/man/ja/man1/vim.1.gz + editor.pl.1.gz /usr/share/man/pl/man1/vim.1.gz + editor.ru.1.gz /usr/share/man/ru/man1/vim.1.gz + +Alternative: /usr/bin/vim.tiny +Priority: 15 +Slaves: + editor.1.gz /usr/share/man/man1/vim.1.gz + editor.da.1.gz /usr/share/man/da/man1/vim.1.gz + editor.de.1.gz /usr/share/man/de/man1/vim.1.gz + editor.fr.1.gz /usr/share/man/fr/man1/vim.1.gz + editor.it.1.gz /usr/share/man/it/man1/vim.1.gz + editor.ja.1.gz /usr/share/man/ja/man1/vim.1.gz + editor.pl.1.gz /usr/share/man/pl/man1/vim.1.gz + editor.ru.1.gz /usr/share/man/ru/man1/vim.1.gz