endZddlZddlZddlmZmZddlmZmZm Z m Z ddl m Z e ddZ Gd dej Zy) zJOSE interfaces.N)MappingSequence)AnyTypeTypeVarUnion)errorsGenericJSONDeSerializableJSONDeSerializable)boundceZdZdZej defdZdefdZe ej de e dede fdZ e de e de eefde fd Zd edefd Zdefd Ze d ddefdZy)r a Interface for (de)serializable JSON objects. Please recall, that standard Python library implements :class:`json.JSONEncoder` and :class:`json.JSONDecoder` that perform translations based on respective :ref:`conversion tables ` that look pretty much like the one below (for complete tables see relevant Python documentation): .. _conversion-table: ====== ====== JSON Python ====== ====== object dict ... ... ====== ====== While the above **conversion table** is about translation of JSON documents to/from the basic Python types only, :class:`JSONDeSerializable` introduces the following two concepts: serialization Turning an arbitrary Python object into Python object that can be encoded into a JSON document. **Full serialization** produces a Python object composed of only basic types as required by the :ref:`conversion table `. **Partial serialization** (accomplished by :meth:`to_partial_json`) produces a Python object that might also be built from other :class:`JSONDeSerializable` objects. deserialization Turning a decoded Python object (necessarily one of the basic types as required by the :ref:`conversion table `) into an arbitrary Python object. Serialization produces **serialized object** ("partially serialized object" or "fully serialized object" for partial and full serialization respectively) and deserialization produces **deserialized object**, both usually denoted in the source code as ``jobj``. Wording in the official Python documentation might be confusing after reading the above, but in the light of those definitions, one can view :meth:`json.JSONDecoder.decode` as decoder and deserializer of basic types, :meth:`json.JSONEncoder.default` as serializer of basic types, :meth:`json.JSONEncoder.encode` as serializer and encoder of basic types. One could extend :mod:`json` to support arbitrary object (de)serialization either by: - overriding :meth:`json.JSONDecoder.decode` and :meth:`json.JSONEncoder.default` in subclasses - or passing ``object_hook`` argument (or ``object_hook_pairs``) to :func:`json.load`/:func:`json.loads` or ``default`` argument for :func:`json.dump`/:func:`json.dumps`. Interestingly, ``default`` is required to perform only partial serialization, as :func:`json.dumps` applies ``default`` recursively. This is the idea behind making :meth:`to_partial_json` produce only partial serialization, while providing custom :meth:`json_dumps` that dumps with ``default`` set to :meth:`json_dump_default`. To make further documentation a bit more concrete, please, consider the following imaginatory implementation example:: class Foo(JSONDeSerializable): def to_partial_json(self): return 'foo' @classmethod def from_json(cls, jobj): return Foo() class Bar(JSONDeSerializable): def to_partial_json(self): return [Foo(), Foo()] @classmethod def from_json(cls, jobj): return Bar() returnct)aPartially serialize. Following the example, **partial serialization** means the following:: assert isinstance(Bar().to_partial_json()[0], Foo) assert isinstance(Bar().to_partial_json()[1], Foo) # in particular... assert Bar().to_partial_json() != ['foo', 'foo'] :raises josepy.errors.SerializationError: in case of any serialization error. :returns: Partially serializable object. )NotImplementedErrorselfs 3/usr/lib/python3/dist-packages/josepy/interfaces.pyto_partial_jsonz"JSONDeSerializable.to_partial_jsoncs ""##c8dtdtffd |S)aDFully serialize. Again, following the example from before, **full serialization** means the following:: assert Bar().to_json() == ['foo', 'foo'] :raises josepy.errors.SerializationError: in case of any serialization error. :returns: Fully serialized object. objrct|tr|jSt|tr|St|tr|Dcgc] }| c}St|t rt fd|DSt|tr0|jDcic]\}}||c}}S|Scc}wcc}}w)Nc3.K|] }|yw)N).0subobj _serializes r zAJSONDeSerializable.to_json.._serialize..sBFZ/Bs) isinstancer rstrlistrtupleritems)rrkeyvaluers rrz.JSONDeSerializable.to_json.._serializes#12!#"5"5"788#s# C&9<=v 6*==C*BcBBBC)MPYY[YzsE 3E)::YY > Zs C'C )r)rrs @rto_jsonzJSONDeSerializable.to_jsonvs# C C  $rclsjobjc|S)aDeserialize a decoded JSON document. :param jobj: Python object, composed of only other basic data types, as decoded from JSON document. Not necessarily :class:`dict` (as decoded from "JSON object" document). :raises josepy.errors.DeserializationError: if decoding was unsuccessful, e.g. in case of unparseable X509 certificate, or wrong padding in JOSE base64 encoded string, etc. r)r'r(s r from_jsonzJSONDeSerializable.from_jsons "u r json_stringc tj|}|j |S#t$r}tj|d}~wwxYw)z&Deserialize from JSON document string.N)jsonloads ValueErrorr DeserializationErrorr*)r'r+r.errors r json_loadszJSONDeSerializable.json_loadssI  5JJ{+E}}U## 5--e4 4 5s( A AA kwargsc Ftj|fd|ji|S)zsDump to JSON string using proper serializer. :returns: JSON document string. :rtype: str default)r-dumpsjson_dump_default)rr3s r json_dumpszJSONDeSerializable.json_dumpss#zz$I(>(>I&IIrc*|jdddS)zNDump the object to pretty JSON document string. :rtype: str T),z: ) sort_keysindent separators)r8rs rjson_dumps_prettyz$JSONDeSerializable.json_dumps_prettys aKPPr python_objectcpt|tr|jStt |dz)aSerialize Python object. This function is meant to be passed as ``default`` to :func:`json.dump` or :func:`json.dumps`. They call ``default(python_object)`` only for non-basic Python types, so this function necessarily raises :class:`TypeError` if ``python_object`` is not an instance of :class:`IJSONSerializable`. Please read the class docstring for more information. z is not JSON serializable)rr r TypeErrorrepr)r'r@s rr7z$JSONDeSerializable.json_dump_defaults4 m%7 8 002 2D/2MMN NrN)__name__ __module__ __qualname____doc__abcabstractmethodrrr& classmethodrr r*rr bytesr2r8r?r7rrrr r sTl $$$$  @t56cF_"$ + ,$;@e;L$ "$$J3J3JQ3QO.BOsOOr) metaclass)rGrHr-collections.abcrrtypingrrrrjosepyr r ABCMetar rrrrQs= -,,#$?G[\JO3;;JOr