fauna.encoding.decoder
1import base64 2from typing import Any, List, Union 3 4from iso8601 import parse_date 5 6from fauna.query.models import Module, DocumentReference, Document, NamedDocument, NamedDocumentReference, Page, \ 7 NullDocument, EventSource 8 9 10class FaunaDecoder: 11 """Supports the following types: 12 13 +--------------------+---------------+ 14 | Python | Fauna | 15 +====================+===============+ 16 | dict | object | 17 +--------------------+---------------+ 18 | list, tuple | array | 19 +--------------------+---------------+ 20 | str | string | 21 +--------------------+---------------+ 22 | int | @int | 23 +--------------------+---------------+ 24 | int | @long | 25 +--------------------+---------------+ 26 | float | @double | 27 +--------------------+---------------+ 28 | datetime.datetime | @time | 29 +--------------------+---------------+ 30 | datetime.date | @date | 31 +--------------------+---------------+ 32 | True | true | 33 +--------------------+---------------+ 34 | False | false | 35 +--------------------+---------------+ 36 | None | null | 37 +--------------------+---------------+ 38 | bytearray | @bytes | 39 +--------------------+---------------+ 40 | *DocumentReference | @ref | 41 +--------------------+---------------+ 42 | *Document | @doc | 43 +--------------------+---------------+ 44 | Module | @mod | 45 +--------------------+---------------+ 46 | Page | @set | 47 +--------------------+---------------+ 48 | EventSource | @stream | 49 +--------------------+---------------+ 50 51 """ 52 53 @staticmethod 54 def decode(obj: Any): 55 """Decodes supported objects from the tagged typed into untagged. 56 57 Examples: 58 - { "@int": "100" } decodes to 100 of type int 59 - { "@double": "100" } decodes to 100.0 of type float 60 - { "@long": "100" } decodes to 100 of type int 61 - { "@time": "..." } decodes to a datetime 62 - { "@date": "..." } decodes to a date 63 - { "@doc": ... } decodes to a Document or NamedDocument 64 - { "@ref": ... } decodes to a DocumentReference or NamedDocumentReference 65 - { "@mod": ... } decodes to a Module 66 - { "@set": ... } decodes to a Page 67 - { "@stream": ... } decodes to an EventSource 68 - { "@bytes": ... } decodes to a bytearray 69 70 :param obj: the object to decode 71 """ 72 return FaunaDecoder._decode(obj) 73 74 @staticmethod 75 def _decode(o: Any, escaped: bool = False): 76 if isinstance(o, (str, bool, int, float)): 77 return o 78 elif isinstance(o, list): 79 return FaunaDecoder._decode_list(o) 80 elif isinstance(o, dict): 81 return FaunaDecoder._decode_dict(o, escaped) 82 83 @staticmethod 84 def _decode_list(lst: List): 85 return [FaunaDecoder._decode(i) for i in lst] 86 87 @staticmethod 88 def _decode_dict(dct: dict, escaped: bool): 89 keys = dct.keys() 90 91 # If escaped, everything is user-specified 92 if escaped: 93 return {k: FaunaDecoder._decode(v) for k, v in dct.items()} 94 95 if len(keys) == 1: 96 if "@int" in keys: 97 return int(dct["@int"]) 98 if "@long" in keys: 99 return int(dct["@long"]) 100 if "@double" in dct: 101 return float(dct["@double"]) 102 if "@object" in dct: 103 return FaunaDecoder._decode(dct["@object"], True) 104 if "@mod" in dct: 105 return Module(dct["@mod"]) 106 if "@time" in dct: 107 return parse_date(dct["@time"]) 108 if "@date" in dct: 109 return parse_date(dct["@date"]).date() 110 if "@bytes" in dct: 111 bts = base64.b64decode(dct["@bytes"]) 112 return bytearray(bts) 113 if "@doc" in dct: 114 value = dct["@doc"] 115 if isinstance(value, str): 116 # Not distinguishing between DocumentReference and NamedDocumentReference because this shouldn't 117 # be an issue much longer 118 return DocumentReference.from_string(value) 119 120 contents = FaunaDecoder._decode(value) 121 122 if "id" in contents and "coll" in contents and "ts" in contents: 123 doc_id = contents.pop("id") 124 doc_coll = contents.pop("coll") 125 doc_ts = contents.pop("ts") 126 127 return Document( 128 id=doc_id, 129 coll=doc_coll, 130 ts=doc_ts, 131 data=contents, 132 ) 133 elif "name" in contents and "coll" in contents and "ts" in contents: 134 doc_name = contents.pop("name") 135 doc_coll = contents.pop("coll") 136 doc_ts = contents.pop("ts") 137 138 return NamedDocument( 139 name=doc_name, 140 coll=doc_coll, 141 ts=doc_ts, 142 data=contents, 143 ) 144 else: 145 # Unsupported document reference. Return the unwrapped value to futureproof. 146 return contents 147 148 if "@ref" in dct: 149 value = dct["@ref"] 150 if "id" not in value and "name" not in value: 151 # Unsupported document reference. Return the unwrapped value to futureproof. 152 return value 153 154 col = FaunaDecoder._decode(value["coll"]) 155 doc_ref: Union[DocumentReference, NamedDocumentReference] 156 157 if "id" in value: 158 doc_ref = DocumentReference(col, value["id"]) 159 else: 160 doc_ref = NamedDocumentReference(col, value["name"]) 161 162 if "exists" in value and not value["exists"]: 163 cause = value["cause"] if "cause" in value else None 164 return NullDocument(doc_ref, cause) 165 166 return doc_ref 167 168 if "@set" in dct: 169 value = dct["@set"] 170 if isinstance(value, str): 171 return Page(after=value) 172 173 after = value["after"] if "after" in value else None 174 data = FaunaDecoder._decode(value["data"]) if "data" in value else None 175 176 return Page(data=data, after=after) 177 178 if "@stream" in dct: 179 return EventSource(dct["@stream"]) 180 181 return {k: FaunaDecoder._decode(v) for k, v in dct.items()}
11class FaunaDecoder: 12 """Supports the following types: 13 14 +--------------------+---------------+ 15 | Python | Fauna | 16 +====================+===============+ 17 | dict | object | 18 +--------------------+---------------+ 19 | list, tuple | array | 20 +--------------------+---------------+ 21 | str | string | 22 +--------------------+---------------+ 23 | int | @int | 24 +--------------------+---------------+ 25 | int | @long | 26 +--------------------+---------------+ 27 | float | @double | 28 +--------------------+---------------+ 29 | datetime.datetime | @time | 30 +--------------------+---------------+ 31 | datetime.date | @date | 32 +--------------------+---------------+ 33 | True | true | 34 +--------------------+---------------+ 35 | False | false | 36 +--------------------+---------------+ 37 | None | null | 38 +--------------------+---------------+ 39 | bytearray | @bytes | 40 +--------------------+---------------+ 41 | *DocumentReference | @ref | 42 +--------------------+---------------+ 43 | *Document | @doc | 44 +--------------------+---------------+ 45 | Module | @mod | 46 +--------------------+---------------+ 47 | Page | @set | 48 +--------------------+---------------+ 49 | EventSource | @stream | 50 +--------------------+---------------+ 51 52 """ 53 54 @staticmethod 55 def decode(obj: Any): 56 """Decodes supported objects from the tagged typed into untagged. 57 58 Examples: 59 - { "@int": "100" } decodes to 100 of type int 60 - { "@double": "100" } decodes to 100.0 of type float 61 - { "@long": "100" } decodes to 100 of type int 62 - { "@time": "..." } decodes to a datetime 63 - { "@date": "..." } decodes to a date 64 - { "@doc": ... } decodes to a Document or NamedDocument 65 - { "@ref": ... } decodes to a DocumentReference or NamedDocumentReference 66 - { "@mod": ... } decodes to a Module 67 - { "@set": ... } decodes to a Page 68 - { "@stream": ... } decodes to an EventSource 69 - { "@bytes": ... } decodes to a bytearray 70 71 :param obj: the object to decode 72 """ 73 return FaunaDecoder._decode(obj) 74 75 @staticmethod 76 def _decode(o: Any, escaped: bool = False): 77 if isinstance(o, (str, bool, int, float)): 78 return o 79 elif isinstance(o, list): 80 return FaunaDecoder._decode_list(o) 81 elif isinstance(o, dict): 82 return FaunaDecoder._decode_dict(o, escaped) 83 84 @staticmethod 85 def _decode_list(lst: List): 86 return [FaunaDecoder._decode(i) for i in lst] 87 88 @staticmethod 89 def _decode_dict(dct: dict, escaped: bool): 90 keys = dct.keys() 91 92 # If escaped, everything is user-specified 93 if escaped: 94 return {k: FaunaDecoder._decode(v) for k, v in dct.items()} 95 96 if len(keys) == 1: 97 if "@int" in keys: 98 return int(dct["@int"]) 99 if "@long" in keys: 100 return int(dct["@long"]) 101 if "@double" in dct: 102 return float(dct["@double"]) 103 if "@object" in dct: 104 return FaunaDecoder._decode(dct["@object"], True) 105 if "@mod" in dct: 106 return Module(dct["@mod"]) 107 if "@time" in dct: 108 return parse_date(dct["@time"]) 109 if "@date" in dct: 110 return parse_date(dct["@date"]).date() 111 if "@bytes" in dct: 112 bts = base64.b64decode(dct["@bytes"]) 113 return bytearray(bts) 114 if "@doc" in dct: 115 value = dct["@doc"] 116 if isinstance(value, str): 117 # Not distinguishing between DocumentReference and NamedDocumentReference because this shouldn't 118 # be an issue much longer 119 return DocumentReference.from_string(value) 120 121 contents = FaunaDecoder._decode(value) 122 123 if "id" in contents and "coll" in contents and "ts" in contents: 124 doc_id = contents.pop("id") 125 doc_coll = contents.pop("coll") 126 doc_ts = contents.pop("ts") 127 128 return Document( 129 id=doc_id, 130 coll=doc_coll, 131 ts=doc_ts, 132 data=contents, 133 ) 134 elif "name" in contents and "coll" in contents and "ts" in contents: 135 doc_name = contents.pop("name") 136 doc_coll = contents.pop("coll") 137 doc_ts = contents.pop("ts") 138 139 return NamedDocument( 140 name=doc_name, 141 coll=doc_coll, 142 ts=doc_ts, 143 data=contents, 144 ) 145 else: 146 # Unsupported document reference. Return the unwrapped value to futureproof. 147 return contents 148 149 if "@ref" in dct: 150 value = dct["@ref"] 151 if "id" not in value and "name" not in value: 152 # Unsupported document reference. Return the unwrapped value to futureproof. 153 return value 154 155 col = FaunaDecoder._decode(value["coll"]) 156 doc_ref: Union[DocumentReference, NamedDocumentReference] 157 158 if "id" in value: 159 doc_ref = DocumentReference(col, value["id"]) 160 else: 161 doc_ref = NamedDocumentReference(col, value["name"]) 162 163 if "exists" in value and not value["exists"]: 164 cause = value["cause"] if "cause" in value else None 165 return NullDocument(doc_ref, cause) 166 167 return doc_ref 168 169 if "@set" in dct: 170 value = dct["@set"] 171 if isinstance(value, str): 172 return Page(after=value) 173 174 after = value["after"] if "after" in value else None 175 data = FaunaDecoder._decode(value["data"]) if "data" in value else None 176 177 return Page(data=data, after=after) 178 179 if "@stream" in dct: 180 return EventSource(dct["@stream"]) 181 182 return {k: FaunaDecoder._decode(v) for k, v in dct.items()}
Supports the following types:
+--------------------+---------------+ | Python | Fauna | +====================+===============+ | dict | object | +--------------------+---------------+ | list, tuple | array | +--------------------+---------------+ | str | string | +--------------------+---------------+ | int | @int | +--------------------+---------------+ | int | @long | +--------------------+---------------+ | float | @double | +--------------------+---------------+ | datetime.datetime | @time | +--------------------+---------------+ | datetime.date | @date | +--------------------+---------------+ | True | true | +--------------------+---------------+ | False | false | +--------------------+---------------+ | None | null | +--------------------+---------------+ | bytearray | @bytes | +--------------------+---------------+ | *DocumentReference | @ref | +--------------------+---------------+ | *Document | @doc | +--------------------+---------------+ | Module | @mod | +--------------------+---------------+ | Page | @set | +--------------------+---------------+ | EventSource | @stream | +--------------------+---------------+
54 @staticmethod 55 def decode(obj: Any): 56 """Decodes supported objects from the tagged typed into untagged. 57 58 Examples: 59 - { "@int": "100" } decodes to 100 of type int 60 - { "@double": "100" } decodes to 100.0 of type float 61 - { "@long": "100" } decodes to 100 of type int 62 - { "@time": "..." } decodes to a datetime 63 - { "@date": "..." } decodes to a date 64 - { "@doc": ... } decodes to a Document or NamedDocument 65 - { "@ref": ... } decodes to a DocumentReference or NamedDocumentReference 66 - { "@mod": ... } decodes to a Module 67 - { "@set": ... } decodes to a Page 68 - { "@stream": ... } decodes to an EventSource 69 - { "@bytes": ... } decodes to a bytearray 70 71 :param obj: the object to decode 72 """ 73 return FaunaDecoder._decode(obj)
Decodes supported objects from the tagged typed into untagged.
Examples: - { "@int": "100" } decodes to 100 of type int - { "@double": "100" } decodes to 100.0 of type float - { "@long": "100" } decodes to 100 of type int - { "@time": "..." } decodes to a datetime - { "@date": "..." } decodes to a date - { "@doc": ... } decodes to a Document or NamedDocument - { "@ref": ... } decodes to a DocumentReference or NamedDocumentReference - { "@mod": ... } decodes to a Module - { "@set": ... } decodes to a Page - { "@stream": ... } decodes to an EventSource - { "@bytes": ... } decodes to a bytearray
Parameters
- obj: the object to decode