mirror of https://github.com/seebye/ueberzug
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.
146 lines
3.5 KiB
Python
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
|