diff --git a/pyinfra/parser/__init__.py b/pyinfra/parser/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyinfra/parser/blob_parser.py b/pyinfra/parser/blob_parser.py new file mode 100644 index 0000000..1a3e21e --- /dev/null +++ b/pyinfra/parser/blob_parser.py @@ -0,0 +1,14 @@ +import abc + + +class ParsingError(Exception): + pass + + +class BlobParser(abc.ABC): + @abc.abstractmethod + def parse(self, blob: bytes): + pass + + def __call__(self, blob: bytes): + return self.parse(blob) diff --git a/pyinfra/parser/parser_composer.py b/pyinfra/parser/parser_composer.py new file mode 100644 index 0000000..8bb16a6 --- /dev/null +++ b/pyinfra/parser/parser_composer.py @@ -0,0 +1,52 @@ +from funcy import rcompose + +from pyinfra.parser.blob_parser import ParsingError + + +class Either: + def __init__(self, item): + self.item = item + + def bind(self): + return self.item + + +class Left(Either): + pass + + +class Right(Either): + pass + + +class EitherParserWrapper: + def __init__(self, parser): + self.parser = parser + + def parse(self, item: Either): + if isinstance(item, Left): + + try: + return Right(self.parser(item.bind())) + except ParsingError: + return item + + elif isinstance(item, Right): + return item + + else: + return self.parse(Left(item)) + + def __call__(self, item): + return self.parse(item) + + +class EitherParserComposer: + def __init__(self, *parsers): + self.parser = rcompose(*map(EitherParserWrapper, parsers)) + + def parse(self, item): + return self.parser(item).item + + def __call__(self, item): + return self.parse(item) diff --git a/pyinfra/parser/parsers/__init__.py b/pyinfra/parser/parsers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyinfra/parser/parsers/identity.py b/pyinfra/parser/parsers/identity.py new file mode 100644 index 0000000..e70f75d --- /dev/null +++ b/pyinfra/parser/parsers/identity.py @@ -0,0 +1,7 @@ +from pyinfra.parser.blob_parser import BlobParser + + +class IdentityBlobParser(BlobParser): + + def parse(self, data: bytes): + return data diff --git a/pyinfra/parser/parsers/json.py b/pyinfra/parser/parsers/json.py new file mode 100644 index 0000000..b666ce1 --- /dev/null +++ b/pyinfra/parser/parsers/json.py @@ -0,0 +1,19 @@ +import json + +from pyinfra.parser.blob_parser import BlobParser, ParsingError +from pyinfra.server.packing import string_to_bytes + + +class JsonBlobParser(BlobParser): + + def parse(self, data: bytes): + try: + data = data.decode() + data = json.loads(data) + except (UnicodeDecodeError, json.JSONDecodeError, AttributeError) as err: + raise ParsingError from err + + if "data" in data: + data["data"] = string_to_bytes(data["data"]) + + return data diff --git a/pyinfra/parser/parsers/string.py b/pyinfra/parser/parsers/string.py new file mode 100644 index 0000000..9c4bb0f --- /dev/null +++ b/pyinfra/parser/parsers/string.py @@ -0,0 +1,9 @@ +from pyinfra.parser.blob_parser import BlobParser, ParsingError + + +class StringBlobParser(BlobParser): + def parse(self, data: bytes): + try: + return data.decode() + except Exception as err: + raise ParsingError from err diff --git a/test/parser/__init__.py b/test/parser/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/parser/parser_test.py b/test/parser/parser_test.py new file mode 100644 index 0000000..6d93d2e --- /dev/null +++ b/test/parser/parser_test.py @@ -0,0 +1,34 @@ +import json + +from pyinfra.parser.parser_composer import EitherParserComposer +from pyinfra.parser.parsers.identity import IdentityBlobParser +from pyinfra.parser.parsers.json import JsonBlobParser +from pyinfra.parser.parsers.string import StringBlobParser + + +def test_json_parser(): + d = {"a": 1} + assert JsonBlobParser()(json.dumps(d).encode()) == d + + +def test_string_parser(): + a = "a" + assert StringBlobParser()(a.encode()) == a + + +def test_identity_parser(): + a = "a" + assert IdentityBlobParser()(a.encode()) == a.encode() + + +def test_either_parser_composer(): + parser = EitherParserComposer(JsonBlobParser(), StringBlobParser(), IdentityBlobParser()) + + d = {"a": 1} + assert parser(json.dumps(d).encode()) == d + + a = "a" + assert parser(a.encode()) == a + + a = 1 + assert parser(a) == a