Jsonable API¶
Module for the jsonable interface
-
class
pheres._jsonable.
jsonable
(cls: type = None, /, after: Union[str, Iterable[str]] = (), internal: str = None, only_marked: bool = False)¶ Class decorator to make a class jsonable
This decorator adds members to the decorated class to serialize or deserialize instance from JSON format. Class decorated this way are called
jsonable
classess. The members added by this decorator are described inJsonableDummy
. Some may not be added by certain usage of this decorator, see below.Jsonable classes are typed. This means the values found in JSON must have the correct types when deserializing. Types are specified in python code by using PEP 526 type annotations. No typecheck occurs on serialization, as python’s type annotations are not binding. Pheres trusts that the code follows the annotations it exposes, but does not enforce it. Ill-implemented code may well crash when deserializing instances that did not abide to their annotations.
@jsonable
can be parametrized by type-hints, much like types from thetyping
module (e.g.@jsonable[int]
). This will produce various types of jsonable classes. Refer to the Usage section below for details.There are four types of jsonable classes: jsonable value, jsonable array, jsonable dict and jsonable object. These jsonable class have different JSON representation and are documented below.
This class decorator is fully compatible with the
dataclasses
module and theattr.s
decorator, though it must be used as the inner-most decorator (that is, it must be executed first on the class). It will raise an error if this is not the case.- Parameters
after (Union[str, Iterable[str]]) –
Modify the decorated class only after the listed member become available in the class’ module. This allows for circular definitions. When the class is modified, the class object is retrieved again from the module it was defined in. This makes
after
compatibles with decorators that replace the class object with another one, such asattr.s
in certain circumstances.Checks for dependencies are performed after each use of the
@jsonable
decorator. If your class is the last decorated one in the file, calljsonable.decorate_delayed
to modify it before usage.internal (str) – For jsonable value, array of dict. Name of the attribute where the JSON value is stored, allowing Pheres to make add the
to_json
method to the decorated classonly_marked (bool) – For jsonable object only. If
True
, all annotated attributes are used. IfFalse
, only marked attributes are used. See jsonable objects below. Attributes lacking a PEP 526 annotation are always ignored.
- Raises
TypeError – an argument has a wrong type
JsonableError – the decorator is not the innermost with respect to
dataclasses.dataclass
orattr.s
- Jsonable values:
Jsonable values are represented by a single JSON value (null, a number, a string). To make a jsonable value by decorating a class with
@jsonable
, the class must:have an
__init__
method accepting the callClass(value)
, where value is the JSON value for an instance of the class (this is used for deserialization)implement the
to_json(self)
method, that returns the value to represents the instance with in JSON (e.g.None
, anint
or astr
). This is because Pheres cannot know what the class does with the value, like it does with jsonable objects.
See the Usage section below for how to make jsonable values
- Jsonable arrays:
Jsonable arrays are represented by a JSON array. There are two kind of arrays, fixed-length arrays and arbitrary length arrays. To make a jsonable array by decorating a class with
@jsonable
, the class must:have an
__init__
method that accept the callClass(*array)
, wherearray
is the python list for the JSON array. This means:For fixed-length array, a signature
__init__(self, v1, v2, ...)
with a fixed number of arguments is acceptedFor arbitrary length array, the number of arguments is not known in advance, so the signature must be similar to
__init__(self, *array)
(this signature is also valid for fixed-length arrays as it does accept the call above).
implement the
to_json(self)
method, that returns the pythonlist
that represents the instance in JSON. This is because Pheres cannot know what the class does with the values like it does with jsonable objects.
See the Usage section below for how to make jsonable arrays
- Jsonable dicts:
Jsonable dicts are represented by a JSON Object with arbitrary key-value pairs. For JSON object with known key-value pairs, see jsonable objects below. To make a jsonable dict by decorating a class with
@jsonable
, the class must:have an
__init__
method that accept the callClass(**object)
, whereobject
is the python dict representing the instance in JSON. The only signature that supports that in python is of the sort__init__(self, **object)
(the actual name of theobject
parameter can vary). Optional arguments may be added, but all key-value pairs found in the deserialized JSON will be passed as keyword arguments. This is used for deserialization.implement the
to_json(self)
method, that returns a pythondict
that represents the instance in JSON. This is because Pheres cannot know what the class does with the key-value pairs like it does with jsonable objects.
See the Usage section below for how to make jsonable dicts
- Jsonable objects:
Jsonable objects are represented by a JSON Object with known key-value pairs. The key-value paires are obtained from the class by inspecting PEP 526 annotations. Attributes must be annotated to be considered by Pheres, then:
If
only_marked
isFalse
(the default), all annotated attributes are usedIf
only_marked
isFalse
, attributes must be marked for use (seeMarked
andmarked
)
Irrespective of the value of
only_marked
,Marked
andmarked
can always be used for refined control (see their documentation).If an attribute has a value in the class body, it is taken to be the default value of this attribute. The attribute becomes optional in the JSON representation. Defaults are always deep-copied when instantiating the class from a JSON representation, so
list
anddict
are safe to use.To make a jsonable object by decorating a class with
@jsonable
, the class must:have an
__init__
method that accept the callClass(attr1=val1, attr2=val2, ...)
where attributes values are passed as keyword arguments. This is automatically the case if you useddataclasses.dataclass
orattr.s
and is the intended usage for this functuonality.
In particular, jsonable object do not need to implement a
to_json(self)
method, as Pheres knows exactly what attributes to use. This is in contrast with other jsonable classes.See the Usage section below for how to make jsonable objects
- Usages:
There are several usage of this decorator.
- Type parametrization:
To produce jsonable values, arrays or dict, the decorator must be parametrized by a type-hint. Any valid type-hint in JSON context can be used. The following syntax may be used:
@jsonable[T] @jsonable.Value[T] @jsonable.Array[T] @jsonable.Dict[T]
The first forms encompasses the following three: If the passed type
T
is a JSON value, it will produce a jsonable value after decorating the class. IfT
istyping.List
ortyping.Tuple
(orlist
ortuple
since python 3.9), it will produce a jsonable array. Finally, ifT
istyping.Dict
(ordict
), it will produce a jsonable dict. If you wish to produce a jsonable object, do not parametrize the decorator.The type must be fully parametrized, to provide the type of the JSON representation down to the last value.
typing.Union
can be used. This means that:@jsonable[List] # not valid @jsonable[List[int]] # valid @jsonable[List[Union[int, str]]] # valid
For jsonable arrays,
typing.List
will produce an arbitrary-length array, whiletyping.Tuple
will produce a fixed-length array, unless an ellipsis is used (e.g.jsonable[Tuple[int, Ellipsis]]
). The literal ellipsis...
may be used, but this is avoided in this documentation to prevent confusion with the meaning that more arguments can be provided.@jsonable.Value[T, ...]
is equivalent tojsonable[Union[T, ...]]
(The...
here is not python ellipsis but indicates that more than one types may be passed).@jsonable.Array[T, ...]
is equivalent tojsonable[Tuple[T, ...]]
(where the...
indicates that more than one argument may be passed). This produces a fixed-length array – useEllispis
in second position to procude an arbitrary-length array.@jsonable.Dict[T, ...]
is equivalent to@jsonable[dict[str, Union[T, ...]]]
and produces a jsonable dict. To produce a jsonable object, do not parametrize the decorator.- Arguments:
If specified, arguments must be provided after the type parametrization.
after
can be used for all jsonable classes, butonly_marked
only has an effect on jsonable objects.
Notes
@jsonable
only add members that are not explicitely defined by the decorated class (inherited implementation are ignored). This means you can overwrite the default behavior if necessary.When parametrized by a type or passed arguments, this decorator returns an object that may be re-used. This means that the following code is valid:
from pheres import jsonable, Marked my_decorator = jsonable(only_marked=True) @my_decorator class MyClass: python: int json: Marked[int]
-
Value[T, ...]
Shortcut for
jsonable[Union[T, ...]]
-
Array[T, ...]
Shortcut for
jsonable[Tuple[T, ...]]
-
Dict[T, ...]
Shortcut for
jsonable[Dict[str, Union[T, ...]]]
-
class
pheres._jsonable.
JsonableDummy
¶ Bases:
object
Dummy class providing dummy members for all members added by
@jsonable
. Allows type checkers and linters to detect said attributes-
Decoder
¶ alias of
pheres._datatypes.UsableDecoder
-
classmethod
from_json
(obj: Any) → AnyClass¶ Converts a JSON file, string or object to an instance of that class
-
to_json
() → JSONType¶ Converts an instance of that class to a JSON object
-
-
class
pheres._jsonable.
JsonMark
(key: Optional[str] = None, json_only: bool = MISSING)¶ Bases:
object
Annotation for jsonized arguments that provides more control on the jsonized attribute behavior. All arguments are optional.
- Parameters
key – Set the name of the key in JSON for that attribute. Defaults to the name of the attribute in python
json_only – Make the attribute only present in JSON. The attribute must have a default value or be a
typing.Literal
of a single value. The attribute is removed from the class’ annotations at runtime Defaults toTrue
for Literals of a single value;False
for all other types
-
pheres._jsonable.
Marked
¶ Simple type alias to quickly mark an attribute as jsonized
Marked[T]
is equivalent toAnnotated[T, JsonMark()]
alias of TypeT
-
pheres._jsonable.
marked
(tp: TypeHint, /, **kwargs) → TypeHint¶ Shortcut for
Annotated[T, JsonMark(**kwargs)]
See
JsonMark
for a list of supported keyword arguments.marked
may not be compatible with type checkers due to being a runtime definition- Parameters
tp – Type hint to mark
**kwargs – additional info to pass to
JsonMark
See also
-
class
pheres._jsonable.
JSONableEncoder
(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)¶ Bases:
json.encoder.JSONEncoder
JSONEncoder subclass that supports jsonable classes
-
default
(obj: object)¶ Overrides
json.JSONEncoder.default
to support jsonable classes
-
-
pheres._jsonable.
dump
(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)¶ Simple wrapper around
json.dump
that usesJSONableEncoder
as the default encoder.- Wrapped function docstring:
Serialize
obj
as a JSON formatted stream tofp
(a.write()
-supporting file-like object).If
skipkeys
is true thendict
keys that are not basic types (str
,int
,float
,bool
,None
) will be skipped instead of raising aTypeError
.If
ensure_ascii
is false, then the strings written tofp
can contain non-ASCII characters if they appear in strings contained inobj
. Otherwise, all such characters are escaped in JSON strings.If
check_circular
is false, then the circular reference check for container types will be skipped and a circular reference will result in anOverflowError
(or worse).If
allow_nan
is false, then it will be aValueError
to serialize out of rangefloat
values (nan
,inf
,-inf
) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (NaN
,Infinity
,-Infinity
).If
indent
is a non-negative integer, then JSON array elements and object members will be pretty-printed with that indent level. An indent level of 0 will only insert newlines.None
is the most compact representation.If specified,
separators
should be an(item_separator, key_separator)
tuple. The default is(', ', ': ')
if indent isNone
and(',', ': ')
otherwise. To get the most compact JSON representation, you should specify(',', ':')
to eliminate whitespace.default(obj)
is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError.If sort_keys is true (default:
False
), then the output of dictionaries will be sorted by key.To use a custom
JSONEncoder
subclass (e.g. one that overrides the.default()
method to serialize additional types), specify it with thecls
kwarg; otherwiseJSONEncoder
is used.
-
pheres._jsonable.
dumps
(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)¶ Simple wrapper around
json.dumps
that usesJSONableEncoder
as the default encoder.- Wrapped function docstring:
Serialize
obj
to a JSON formattedstr
.If
skipkeys
is true thendict
keys that are not basic types (str
,int
,float
,bool
,None
) will be skipped instead of raising aTypeError
.If
ensure_ascii
is false, then the return value can contain non-ASCII characters if they appear in strings contained inobj
. Otherwise, all such characters are escaped in JSON strings.If
check_circular
is false, then the circular reference check for container types will be skipped and a circular reference will result in anOverflowError
(or worse).If
allow_nan
is false, then it will be aValueError
to serialize out of rangefloat
values (nan
,inf
,-inf
) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (NaN
,Infinity
,-Infinity
).If
indent
is a non-negative integer, then JSON array elements and object members will be pretty-printed with that indent level. An indent level of 0 will only insert newlines.None
is the most compact representation.If specified,
separators
should be an(item_separator, key_separator)
tuple. The default is(', ', ': ')
if indent isNone
and(',', ': ')
otherwise. To get the most compact JSON representation, you should specify(',', ':')
to eliminate whitespace.default(obj)
is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError.If sort_keys is true (default:
False
), then the output of dictionaries will be sorted by key.To use a custom
JSONEncoder
subclass (e.g. one that overrides the.default()
method to serialize additional types), specify it with thecls
kwarg; otherwiseJSONEncoder
is used.