Guide
exprint offers a flexible API for pretty-printing any Python object.
topojson-rs example
Introduction
Let's take for example the class topojson.TopoJSON from topojson-rs, a Python package written in Rust.
It outputs:
If you try using exprint, it will raise an error:
import topojson
from exprint import exprint
topology = topojson.read("./counties-10m.json")
exprint(topology, max_elements=10)
It raises:
TopoJSON class
That's is because TopoJSON is not supported by default by exprint (see supported default objects).
In topojson package, a topojson.TopoJSON has four attributes:
transform:Optional[topojson.Transform]objects:dict[str, Geometry]bbox:list[float]arcs:list[list[list[int]]]
So we need to create a function for formatting a topojson.TopoJSON object.
Since topojson.TopoJSON is a class, we are going to use Formatter.format_class method to get a FormatClass class for formatting the class.
from exprint import Format, Formatter, dispatch_obj
def format_topojson(obj: topojson.TopoJSON, f: Formatter) -> Format:
return (
f.format_class("TopoJSON") # (1)!
.field("transform", obj.transform)
.field("objects", obj.objects)
.field("bbox", obj.bbox)
.field("arcs", obj.arcs)
)
# Registers the `format_topojson` function
dispatch_obj(topojson.TopoJSON, format_topojson) # (2)!
- See
FormatClassfor more details. - See
dispatch_objfor more details.
Let's try now:
import topojson
from exprint import exprint
topology = topojson.read("./counties-10m.json")
def format_topojson(...):
# ...
dispatch_obj(topojson.TopoJSON, format_topojson)
exprint(topology, max_elements=10)
Again it raises a new error:
Transform class
Alright, we need also to create a function for formatting a topojson.Transform object.
In topojson package, a topojson.Transform has two attributes:
scale:list[float]translate:list[float]
def format_transform(obj: topojson.Transform, f: Formatter) -> Format:
return (
f.format_class("Transform")
.field("scale", obj.scale)
.field("translate", obj.translate)
)
# Registers the `format_transform` function
dispatch_obj(topojson.Transform, format_transform)
Let's try with this new format function:
GeometryCollection class
We face a new issue. What is a Geometry_GeometryCollection ?
To keep explanation simple, Rust supports enum structures which are converted into a range of multiple classes.
In the case of topojson package, a Geometry is a union of multiple classes (see documentation).
However, topojson does not allow the user to access to the Geometry type or more precisely the Geometry_GeometryCollection class.
So we are going to create a format function and use "Geometry_GeometryCollection" instead of topojson.Geometry_GeometryCollection:
def format_geometry_collection(
obj: "Geometry_GeometryCollection", f: Formatter
) -> Format:
return f.format_class("GeometryCollection").field("geometries", obj.geometries)
dispatch_obj("Geometry_GeometryCollection", format_geometry_collection)
Now we should be done:
import topojson
from exprint import exprint
topology = topojson.read("./counties-10m.json")
def format_topojson(...):
# ...
def format_transform(...):
# ...
def format_geometry_collection(...):
# ...
dispatch_obj(topojson.TopoJSON, format_topojson)
dispatch_obj(topojson.Transform, format_transform)
dispatch_obj("Geometry_GeometryCollection", format_geometry_collection)
exprint(topology, max_elements=10)
It worked !
TopoJSON {
transform: Transform {
scale: [ 0.003589293992939929, 0.0008590596905969058 ],
translate: [ -179.14734, -14.552549 ],
},
objects: {
"states": GeometryCollection { geometries: [list] },
"nation": GeometryCollection { geometries: [list] },
"counties": GeometryCollection { geometries: [list] },
},
bbox: [ -179.14734, -14.552549, 179.77847, 71.352561 ],
arcs: [
[ [list], [list] ],
[ [list], [list], [list] ],
[ [list], [list] ],
[ [list], [list], [list], [list] ],
[ [list], [list] ],
[ [list], [list] ],
[ [list], [list] ],
[
[list], [list], [list], [list], [list], [list], [list], [list], [list], [list],
... 18 more items
],
[ [list], [list] ],
[
[list], [list], [list], [list], [list], [list], [list], [list], [list], [list],
... 2 more items
],
... 9859 more items
],
}
Info
As it was introduced, Geometry is a union of classes (enumeration in Rust). Thus, other Geometry classes (Geometry_Point, Geometry_MultiPoint, Geometry_LineString, Geometry_MultiLineString, Geometry_Polygon and Geometry_MultiPolygon) must have their own format functions if you want to format correctly any topojson.TopoJSON class.
Complete code
import io
import json
import sys
import topojson
from exprint import Format, Formatter, dispatch_obj, exprint
topology = topojson.read("./counties-10m.json")
def format_topojson(obj: topojson.TopoJSON, f: Formatter) -> Format:
return (
f.format_class("TopoJSON")
.field("transform", obj.transform)
.field("objects", obj.objects)
.field("bbox", obj.bbox)
.field("arcs", obj.arcs)
)
def format_transform(obj: topojson.Transform, f: Formatter) -> Format:
return (
f.format_class("Transform")
.field("scale", obj.scale)
.field("translate", obj.translate)
)
def format_geometry_collection(
obj: "Geometry_GeometryCollection", f: Formatter
) -> Format:
return f.format_class("GeometryCollection").field("geometries", obj.geometries)
dispatch_obj(topojson.TopoJSON, format_topojson)
dispatch_obj(topojson.Transform, format_transform)
dispatch_obj("Geometry_GeometryCollection", format_geometry_collection)
exprint(topology, max_elements=10)