You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I weasled my way in by in fixing typing issues.
In the process, I built up a greater understanding of how a project works and then expanded to proposing/implementing more "fun" features 😉
I'm happy to continue down the road of incremental improvements.
But I can see an see an alternative which - while larger in scope - has clear boundaries and could even be optional for vgplot-python itself.
mosaic-spec-python
I write 0% TS/JS but have understood the majority of mosaic-spec on first look.
There are some more exotic things that haven't made their way to python yet (PEP 827 - Type Manipulation), which I needed to read up on.
However, I believe that it would be possible to define a python counterpart to mosaic-spec, primarily using TypedDict where you have Interfaces1.
To demonstrate, I've (hand-)written vgplot/_interactors.py which semantically identical to:
Provided there is a TypedDict that matches AreaOptions, we can reduce the signature and implementation to 6 lines:
Showarea and friends
from __future__ importannotations# ruff: noqa: N817, F841fromtypingimportTYPE_CHECKING, Any, TypeAlias, TypedDictfromtypingimportLiteralasLifTYPE_CHECKING:
fromtyping_extensionsimportUnpackPlotMarkData: TypeAlias=AnyChannelValueSpec: TypeAlias=AnyChannelValue: TypeAlias=AnyclassMarkData(TypedDict):
# https://github.com/uwdata/mosaic/blob/920bc67bd3134e6df947eeac30e65e2998cd2e23/packages/vgplot/spec/src/spec/marks/Marks.ts#L209-L214data: PlotMarkDataclassMarkDataOptional(TypedDict, total=False):
data: PlotMarkDataclassAreaOptions(TypedDict, total=False):
# https://github.com/uwdata/mosaic/blob/920bc67bd3134e6df947eeac30e65e2998cd2e23/packages/vgplot/spec/src/spec/marks/Area.ts#L6-L44x1: ChannelValueSpecx2: ChannelValueSpecy1: ChannelValueSpecy2: ChannelValueSpecz: ChannelValue# >40 inherited options# ...classAreaData(MarkData, AreaOptions): ...
classArea(AreaData):
mark: L["area"]
classAreaDataOptional(MarkDataOptional, AreaOptions):
mark: L["area"]
# Yes, this is the whole thingdefarea(
data: PlotMarkData|None=None, **kwds: Unpack[AreaOptions]
) ->Area|AreaDataOptional:
return (
{"mark": "area", **kwds}
ifdataisNoneelse {"mark": "area", "data": data, **kwds}
)
defarea_2(**kwds: Unpack[AreaData]) ->AreaData:
returnkwdsdefexample() ->None:
# Even without adding the full spec typing, this already catches things staticallya=area()
b=area([1, 2, 3], x1="a")
c=area(i_dont_exist=1) # pyright: ignore[reportCallIssue]unsafe_1=a["x1"] # pyright: ignore[reportTypedDictNotRequiredAccess]safe_1=a.get("x1", {})
# "No argument provided for required parameter `data` of function `area_2`"d=area_2() # pyright: ignore[reportCallIssue] # ty: ignore[missing-argument]
Not only is the function defintion significantly shorter, but there is now no need for the UNSET sentinel.
Which currently every mark needs to check and remove from 50 fields.
If you consider now that marks.py has 64 functions, all with roughly the same length - maybe you can imagine how many LOC can be saved 😅
That's not my area of interest, but I feel the need to show it as a possibility this unlocks
My first use case would be experimenting with an alternative fluent API, as I personally enjoy writing in that style.
Maybe I can convince you all of this hypothetical API that's worth chaining changing course for vgplot4, but maybe there's no need?
If the representation is fully typed & documented anyone can build an API that makes them happy.
They just need to put all the pieces together and send the spec to vgplot/mosaic_widget 🙂
Mosaic has me more excited about data visualization than I have been in a while!
I've been busy experimenting on my fork and this idea fell out.
Note
Apologies in advance, but I promise this wall of text was 100% human generated ...
Background
While diving through
vgplot-python, the most notable gap for me has been the typing.Why should I care what this guy has to say?
That's where I spent a lot of my time contributing to
altairand more recently innarwhalstoo.I weasled my way in by in fixing typing issues.
In the process, I built up a greater understanding of how a project works and then expanded to proposing/implementing more "fun" features 😉
Naturally, I fell back into this familiar pattern again (#1067 (comment)), (#1067 (comment)), (#1068), (#1069)
I'm happy to continue down the road of incremental improvements.
But I can see an see an alternative which - while larger in scope - has clear boundaries and could even be optional for
vgplot-pythonitself.mosaic-spec-pythonI write 0% TS/JS but have understood the majority of
mosaic-specon first look.There are some more exotic things that haven't made their way to python yet (PEP 827 - Type Manipulation), which I needed to read up on.
However, I believe that it would be possible to define a python counterpart to
mosaic-spec, primarily usingTypedDictwhere you have Interfaces 1.To demonstrate, I've (hand-)written
vgplot/_interactors.pywhich semantically identical to:spec/PlotInteractor.tsspec/interactorsThere are links in that module to other resources on
TypedDict2, including an active transpiler project (ts2python) that may be of interest.Important
While I had my fun writing the
TypedDicts by hand, the proposal would be to generate them with full documentationHow can this benefit
vgplot-python?If improved typing and inline docs don't sell it, let's look at how it can simplify the runtime code too.
Consider,
vg.area, which takes around 50 arguments:Show me the goods
mosaic/packages/vgplot/vgplot-python/vgplot/_generated/marks.py
Lines 19 to 74 in 920bc67
Provided there is a
TypedDictthat matchesAreaOptions, we can reduce the signature and implementation to 6 lines:Show
areaand friendsNot only is the function defintion significantly shorter, but there is now no need for the
UNSETsentinel.Which currently every mark needs to check and remove from 50 fields.
If you consider now that
marks.pyhas 64 functions, all with roughly the same length - maybe you can imagine how many LOC can be saved 😅Why a package?
Good question, I'll start with an uno reverse card (https://idl.uw.edu/mosaic/spec/) 😄
More seriously, what I'm picturing is a lightweight python dependency for projects integrating with Mosaic.
For comparison, schema/latest.json is 8.29MB.
With that, you could get runtime only validation if you also depend on
python-jsonschema/jsonschema(which has more dependencies including a rust extension module).A package which defines the spec in python's type system 3 could be used with a runtime typing package/framework:
There's plenty on the menu
Note
That's not my area of interest, but I feel the need to show it as a possibility this unlocks
My first use case would be experimenting with an alternative fluent API, as I personally enjoy writing in that style.
Maybe I can convince you all of this hypothetical API that's worth
chainingchanging course forvgplot4, but maybe there's no need?If the representation is fully typed & documented anyone can build an API that makes them happy.
They just need to put all the pieces together and send the spec to
vgplot/mosaic_widget🙂Footnotes
Python's
Protocolis conceptually aninterface, butTypedDictwas motivated by representing JSON objects withdict. ↩As a bonus self-plug 😅 - the precursor idea (
vega-lite-schema.json-> altairTypedDict) is here ↩and nothing else ↩
I can't promise that I won't try ↩