You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ueberzug/ueberzug/parser.py

146 lines
3.5 KiB
Python

"""This module defines data parser
which can be used to exchange information between processes.
"""
import json
import abc
import shlex
import itertools
import enum
class Parser:
"""Subclasses of this abstract class define
how to input will be parsed.
"""
@staticmethod
@abc.abstractmethod
def get_name():
"""Returns the constant name which is associated to this parser."""
raise NotImplementedError()
@abc.abstractmethod
def parse(self, line):
"""Parses a line.
Args:
line (str): read line
Returns:
dict containing the key value pairs of the line
"""
raise NotImplementedError()
@abc.abstractmethod
def unparse(self, data):
"""Composes the data to a string
whichs follows the syntax of the parser.
Args:
data (dict): data as key value pairs
Returns:
string
"""
raise NotImplementedError()
class JsonParser(Parser):
"""Parses json input"""
@staticmethod
def get_name():
return 'json'
def parse(self, line):
try:
data = json.loads(line)
if not isinstance(data, dict):
raise ValueError(
'Expected to parse an json object, got ' + line)
return data
except json.JSONDecodeError as error:
raise ValueError(error)
def unparse(self, data):
return json.dumps(data)
class SimpleParser(Parser):
"""Parses key value pairs separated by a tab.
Does not support escaping spaces.
"""
SEPARATOR = '\t'
@staticmethod
def get_name():
return 'simple'
def parse(self, line):
components = line.split(SimpleParser.SEPARATOR)
if len(components) % 2 != 0:
raise ValueError(
'Expected key value pairs, ' +
'but at least one key has no value: ' +
line)
return {
key: value
for key, value in itertools.zip_longest(
components[::2], components[1::2])
}
def unparse(self, data):
return SimpleParser.SEPARATOR.join(
str(key) + SimpleParser.SEPARATOR + str(value.replace('\n', ''))
for key, value in data.items())
class BashParser(Parser):
"""Parses input generated
by dumping associative arrays with `declare -p`.
"""
@staticmethod
def get_name():
return 'bash'
def parse(self, line):
# remove 'typeset -A varname=( ' and ')'
start = line.find('(')
end = line.rfind(')')
if not 0 <= start < end:
raise ValueError(
"Expected input to be formatted like "
"the output of bashs `declare -p` function. "
"Got: " + line)
components = itertools.dropwhile(
lambda text: not text or text[0] != '[',
shlex.split(line[start + 1:end]))
return {
key[1:-1]: value
for pair in components
for key, value in (pair.split('=', maxsplit=1),)
}
def unparse(self, data):
return ' '.join(
'[' + str(key) + ']=' + shlex.quote(value)
for key, value in data.items())
@enum.unique
class ParserOption(str, enum.Enum):
JSON = JsonParser
SIMPLE = SimpleParser
BASH = BashParser
def __new__(cls, parser_class):
inst = str.__new__(cls)
inst._value_ = parser_class.get_name()
inst.parser_class = parser_class
return inst