vgplot 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vgplot-0.1.0/.gitignore +38 -0
- vgplot-0.1.0/PKG-INFO +27 -0
- vgplot-0.1.0/README.md +17 -0
- vgplot-0.1.0/__init__.py +1 -0
- vgplot-0.1.0/api/__init__.py +119 -0
- vgplot-0.1.0/api/data.py +27 -0
- vgplot-0.1.0/api/plot.py +266 -0
- vgplot-0.1.0/api/spec.py +90 -0
- vgplot-0.1.0/api/util.py +14 -0
- vgplot-0.1.0/pyproject.toml +43 -0
- vgplot-0.1.0/schema_wrapper/SchemaBase.py +81 -0
- vgplot-0.1.0/schema_wrapper/__init__.py +0 -0
- vgplot-0.1.0/schema_wrapper/generate_schema_wrapper.py +245 -0
- vgplot-0.1.0/schema_wrapper/generated_classes.py +5872 -0
- vgplot-0.1.0/schema_wrapper/utils.py +345 -0
- vgplot-0.1.0/test/__init__.py +0 -0
- vgplot-0.1.0/test/mini_test.py +661 -0
- vgplot-0.1.0/test/test_full_round_trip.py +65 -0
- vgplot-0.1.0/test/test_generated_classes.py +146 -0
vgplot-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
.mosaic
|
|
3
|
+
.duckdb
|
|
4
|
+
.ipynb_checkpoints
|
|
5
|
+
.idea
|
|
6
|
+
dev/specs
|
|
7
|
+
docs/.vitepress/cache
|
|
8
|
+
docs/.vitepress/dist
|
|
9
|
+
node_modules
|
|
10
|
+
**/dist
|
|
11
|
+
**/test-output
|
|
12
|
+
**/__pycache__
|
|
13
|
+
**/.ruff_cache
|
|
14
|
+
**/.pytest_cache
|
|
15
|
+
**/.mypy_cache
|
|
16
|
+
**/.coverage
|
|
17
|
+
**/.venv
|
|
18
|
+
**/tsconfig.tsbuildinfo
|
|
19
|
+
packages/server/duckdb-server-go/duckdb-server-go
|
|
20
|
+
transitionFolder
|
|
21
|
+
|
|
22
|
+
# Virtual environment
|
|
23
|
+
venv/
|
|
24
|
+
.env/
|
|
25
|
+
|
|
26
|
+
# Conda environment
|
|
27
|
+
conda-env/
|
|
28
|
+
|
|
29
|
+
# Python cache
|
|
30
|
+
__pycache__/
|
|
31
|
+
*.pyc
|
|
32
|
+
|
|
33
|
+
# Test cache
|
|
34
|
+
.pytest_cache/
|
|
35
|
+
.coverage
|
|
36
|
+
|
|
37
|
+
# Python API Generated classes
|
|
38
|
+
packages/vgplot-python/schema_wrapper/generated_classes.py
|
vgplot-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vgplot
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Schema wrapper classes for Mosaic
|
|
5
|
+
Project-URL: homepage, https://github.com/uwdata/mosaic
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Requires-Dist: narwhals
|
|
8
|
+
Requires-Dist: pyyaml
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# Mosaic Python API
|
|
12
|
+
|
|
13
|
+
## Generate the API from the JSON schama
|
|
14
|
+
|
|
15
|
+
`uv run generate-schema-wrapper`.
|
|
16
|
+
|
|
17
|
+
## Run Tests
|
|
18
|
+
|
|
19
|
+
`uv run pytest -v`
|
|
20
|
+
|
|
21
|
+
## Run the ruff formatter
|
|
22
|
+
|
|
23
|
+
`uv run ruff format` and `uv run ruff check`.
|
|
24
|
+
|
|
25
|
+
## Test notebook
|
|
26
|
+
|
|
27
|
+
`uv run jupyter lab` and then open `example.ipynb`.
|
vgplot-0.1.0/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Mosaic Python API
|
|
2
|
+
|
|
3
|
+
## Generate the API from the JSON schama
|
|
4
|
+
|
|
5
|
+
`uv run generate-schema-wrapper`.
|
|
6
|
+
|
|
7
|
+
## Run Tests
|
|
8
|
+
|
|
9
|
+
`uv run pytest -v`
|
|
10
|
+
|
|
11
|
+
## Run the ruff formatter
|
|
12
|
+
|
|
13
|
+
`uv run ruff format` and `uv run ruff check`.
|
|
14
|
+
|
|
15
|
+
## Test notebook
|
|
16
|
+
|
|
17
|
+
`uv run jupyter lab` and then open `example.ipynb`.
|
vgplot-0.1.0/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Mosaic Python package namespace for vgplot authoring API.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from .spec import meta, spec, Spec
|
|
2
|
+
from .data import parquet, table, data
|
|
3
|
+
from .plot import (
|
|
4
|
+
plot,
|
|
5
|
+
from_,
|
|
6
|
+
rule_y,
|
|
7
|
+
rule_x,
|
|
8
|
+
line_y,
|
|
9
|
+
line_x,
|
|
10
|
+
bar_y,
|
|
11
|
+
bar_x,
|
|
12
|
+
area_y,
|
|
13
|
+
area_x,
|
|
14
|
+
text,
|
|
15
|
+
dot,
|
|
16
|
+
density,
|
|
17
|
+
y_grid,
|
|
18
|
+
y_label,
|
|
19
|
+
y_tick_format,
|
|
20
|
+
x_tick_format,
|
|
21
|
+
x_axis,
|
|
22
|
+
y_axis,
|
|
23
|
+
x_label,
|
|
24
|
+
x_label_anchor,
|
|
25
|
+
y_label_anchor,
|
|
26
|
+
r_range,
|
|
27
|
+
color_domain,
|
|
28
|
+
color_scale,
|
|
29
|
+
x_tick_size,
|
|
30
|
+
y_tick_size,
|
|
31
|
+
width,
|
|
32
|
+
height,
|
|
33
|
+
margins,
|
|
34
|
+
vconcat,
|
|
35
|
+
hconcat,
|
|
36
|
+
vspace,
|
|
37
|
+
hspace,
|
|
38
|
+
mark,
|
|
39
|
+
directive,
|
|
40
|
+
slider,
|
|
41
|
+
select,
|
|
42
|
+
checkbox,
|
|
43
|
+
input,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
__all__ = [
|
|
47
|
+
"meta",
|
|
48
|
+
"spec",
|
|
49
|
+
"Spec",
|
|
50
|
+
"parquet",
|
|
51
|
+
"table",
|
|
52
|
+
"data",
|
|
53
|
+
"plot",
|
|
54
|
+
"from_",
|
|
55
|
+
"mark",
|
|
56
|
+
"rule_y",
|
|
57
|
+
"rule_x",
|
|
58
|
+
"line_y",
|
|
59
|
+
"line_x",
|
|
60
|
+
"bar_y",
|
|
61
|
+
"bar_x",
|
|
62
|
+
"area_y",
|
|
63
|
+
"area_x",
|
|
64
|
+
"text",
|
|
65
|
+
"dot",
|
|
66
|
+
"density",
|
|
67
|
+
"y_grid",
|
|
68
|
+
"y_label",
|
|
69
|
+
"y_tick_format",
|
|
70
|
+
"x_tick_format",
|
|
71
|
+
"x_axis",
|
|
72
|
+
"y_axis",
|
|
73
|
+
"x_label",
|
|
74
|
+
"x_label_anchor",
|
|
75
|
+
"y_label_anchor",
|
|
76
|
+
"r_range",
|
|
77
|
+
"color_domain",
|
|
78
|
+
"color_scale",
|
|
79
|
+
"x_tick_size",
|
|
80
|
+
"y_tick_size",
|
|
81
|
+
"width",
|
|
82
|
+
"height",
|
|
83
|
+
"margins",
|
|
84
|
+
"vconcat",
|
|
85
|
+
"hconcat",
|
|
86
|
+
"vspace",
|
|
87
|
+
"hspace",
|
|
88
|
+
"directive",
|
|
89
|
+
"slider",
|
|
90
|
+
"select",
|
|
91
|
+
"checkbox",
|
|
92
|
+
"input",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def __getattr__(name: str):
|
|
97
|
+
"""Dynamic mark/directive factory for names not explicitly exported.
|
|
98
|
+
|
|
99
|
+
Allows any snake_case mark or directive to be used without an explicit
|
|
100
|
+
function definition, e.g.::
|
|
101
|
+
"""
|
|
102
|
+
if name.startswith("_"):
|
|
103
|
+
raise AttributeError(name)
|
|
104
|
+
|
|
105
|
+
from .plot import Directive, Mark
|
|
106
|
+
from .util import camelize
|
|
107
|
+
|
|
108
|
+
camel = camelize(name)
|
|
109
|
+
|
|
110
|
+
def _factory(*args, data=None, **kwargs):
|
|
111
|
+
if len(args) == 1 and data is None and not kwargs:
|
|
112
|
+
# Single positional arg to directive
|
|
113
|
+
return Directive(camel, args[0])
|
|
114
|
+
# otherwise creates a mark
|
|
115
|
+
return Mark(camel, data=data, enc=kwargs if kwargs else None)
|
|
116
|
+
|
|
117
|
+
_factory.__name__ = name
|
|
118
|
+
_factory.__qualname__ = f"vgplot.{name}"
|
|
119
|
+
return _factory
|
vgplot-0.1.0/api/data.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class DataDef:
|
|
9
|
+
payload: Dict[str, Any]
|
|
10
|
+
|
|
11
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
12
|
+
return self.payload
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def parquet(file: str) -> DataDef:
|
|
16
|
+
return DataDef({"type": "parquet", "file": file})
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def table(query: str) -> DataDef:
|
|
20
|
+
return DataDef({"type": "table", "query": query})
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def data(**named_defs: DataDef) -> Dict[str, Any]:
|
|
24
|
+
out: Dict[str, Any] = {}
|
|
25
|
+
for k, v in named_defs.items():
|
|
26
|
+
out[k] = v.to_dict() if hasattr(v, "to_dict") else v
|
|
27
|
+
return out
|
vgplot-0.1.0/api/plot.py
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Dict, List, Optional, Union
|
|
5
|
+
|
|
6
|
+
from .util import camelize, omit_none
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FromRef:
|
|
10
|
+
def __init__(self, name: str):
|
|
11
|
+
self.name = name
|
|
12
|
+
|
|
13
|
+
def to_dict(self) -> Dict[str, str]:
|
|
14
|
+
return {"from": self.name}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def from_(name: str) -> FromRef:
|
|
18
|
+
return FromRef(name)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class Directive:
|
|
23
|
+
key: str
|
|
24
|
+
value: Any
|
|
25
|
+
|
|
26
|
+
def to_kv(self):
|
|
27
|
+
return camelize(self.key), self.value
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class Mark:
|
|
32
|
+
mark: str
|
|
33
|
+
data: Optional[Any] = None
|
|
34
|
+
enc: Optional[Dict[str, Any]] = None
|
|
35
|
+
|
|
36
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
37
|
+
payload: Dict[str, Any] = {"mark": self.mark}
|
|
38
|
+
if self.data is not None:
|
|
39
|
+
payload["data"] = encode_value(self.data)
|
|
40
|
+
if self.enc:
|
|
41
|
+
for k, v in self.enc.items():
|
|
42
|
+
payload[camelize(k)] = encode_value(v)
|
|
43
|
+
return payload
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def encode_value(v: Any) -> Any:
|
|
47
|
+
if isinstance(v, FromRef):
|
|
48
|
+
return v.to_dict()
|
|
49
|
+
if isinstance(v, Mark):
|
|
50
|
+
return v.to_dict()
|
|
51
|
+
if isinstance(v, list):
|
|
52
|
+
return [encode_value(x) for x in v]
|
|
53
|
+
if isinstance(v, dict):
|
|
54
|
+
return {k: encode_value(val) for k, val in v.items()}
|
|
55
|
+
return v
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def plot(*items: Union[Mark, Directive]) -> Dict[str, Any]:
|
|
59
|
+
marks: List[Dict[str, Any]] = []
|
|
60
|
+
directives: Dict[str, Any] = {}
|
|
61
|
+
for item in items:
|
|
62
|
+
if isinstance(item, Mark):
|
|
63
|
+
marks.append(item.to_dict())
|
|
64
|
+
elif isinstance(item, Directive):
|
|
65
|
+
k, v = item.to_kv()
|
|
66
|
+
directives[k] = v
|
|
67
|
+
elif isinstance(item, dict):
|
|
68
|
+
# Treat dicts as already-structured mark entries.
|
|
69
|
+
marks.append(item)
|
|
70
|
+
else:
|
|
71
|
+
raise TypeError(f"Unsupported plot item: {item}")
|
|
72
|
+
root: Dict[str, Any] = {"plot": marks}
|
|
73
|
+
root.update(directives)
|
|
74
|
+
return root
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def directive(key: str, value: Any) -> Directive:
|
|
78
|
+
return Directive(key, value)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def mark(name: str, data: Any = None, **enc: Any) -> Mark:
|
|
82
|
+
return Mark(name, data=data, enc=enc)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def rule_y(data: Any = None, **enc: Any) -> Mark:
|
|
86
|
+
return Mark("ruleY", data=data, enc=enc)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def rule_x(data: Any = None, **enc: Any) -> Mark:
|
|
90
|
+
return Mark("ruleX", data=data, enc=enc)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def line_y(data: Any = None, **enc: Any) -> Mark:
|
|
94
|
+
return Mark("lineY", data=data, enc=enc)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def line_x(data: Any = None, **enc: Any) -> Mark:
|
|
98
|
+
return Mark("lineX", data=data, enc=enc)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def bar_y(data: Any = None, **enc: Any) -> Mark:
|
|
102
|
+
return Mark("barY", data=data, enc=enc)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def bar_x(data: Any = None, **enc: Any) -> Mark:
|
|
106
|
+
return Mark("barX", data=data, enc=enc)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def area_y(data: Any = None, **enc: Any) -> Mark:
|
|
110
|
+
return Mark("areaY", data=data, enc=enc)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def area_x(data: Any = None, **enc: Any) -> Mark:
|
|
114
|
+
return Mark("areaX", data=data, enc=enc)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def text(data: Any = None, **enc: Any) -> Mark:
|
|
118
|
+
return Mark("text", data=data, enc=enc)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def dot(data: Any = None, **enc: Any) -> Mark:
|
|
122
|
+
return Mark("dot", data=data, enc=enc)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def density(data: Any = None, **enc: Any) -> Mark:
|
|
126
|
+
return Mark("density", data=data, enc=enc)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def y_grid(value: bool) -> Directive:
|
|
130
|
+
return Directive("y_grid", value)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def y_label(value: str) -> Directive:
|
|
134
|
+
return Directive("y_label", value)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def y_tick_format(value: str) -> Directive:
|
|
138
|
+
return Directive("y_tick_format", value)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def x_tick_format(value: str) -> Directive:
|
|
142
|
+
return Directive("x_tick_format", value)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def x_axis(value: Any) -> Directive:
|
|
146
|
+
return Directive("x_axis", value)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def y_axis(value: Any) -> Directive:
|
|
150
|
+
return Directive("y_axis", value)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def x_label(value: str) -> Directive:
|
|
154
|
+
return Directive("x_label", value)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def x_label_anchor(value: str) -> Directive:
|
|
158
|
+
return Directive("x_label_anchor", value)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def y_label_anchor(value: str) -> Directive:
|
|
162
|
+
return Directive("y_label_anchor", value)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def r_range(value: Any) -> Directive:
|
|
166
|
+
return Directive("r_range", value)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def color_domain(value: Any) -> Directive:
|
|
170
|
+
return Directive("color_domain", value)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def color_scale(value: Any) -> Directive:
|
|
174
|
+
return Directive("color_scale", value)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def width(value: int) -> Directive:
|
|
178
|
+
return Directive("width", value)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def height(value: int) -> Directive:
|
|
182
|
+
return Directive("height", value)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def margins(
|
|
186
|
+
top: int = None,
|
|
187
|
+
right: int = None,
|
|
188
|
+
bottom: int = None,
|
|
189
|
+
left: int = None,
|
|
190
|
+
**kwargs: Any,
|
|
191
|
+
) -> Directive:
|
|
192
|
+
return Directive(
|
|
193
|
+
"margins",
|
|
194
|
+
omit_none(
|
|
195
|
+
{"top": top, "right": right, "bottom": bottom, "left": left, **kwargs}
|
|
196
|
+
),
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def x_tick_size(value: Any) -> Directive:
|
|
201
|
+
return Directive("x_tick_size", value)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def y_tick_size(value: Any) -> Directive:
|
|
205
|
+
return Directive("y_tick_size", value)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# Layout helpers
|
|
209
|
+
def vconcat(*items: Any) -> Dict[str, Any]:
|
|
210
|
+
return {"vconcat": [encode_value(i) for i in items]}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def hconcat(*items: Any) -> Dict[str, Any]:
|
|
214
|
+
return {"hconcat": [encode_value(i) for i in items]}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def hspace(px: int) -> Dict[str, Any]:
|
|
218
|
+
return {"hspace": px}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def vspace(px: int) -> Dict[str, Any]:
|
|
222
|
+
return {"vspace": px}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# Input helpers
|
|
226
|
+
def input(kind: str, **opts: Any) -> Dict[str, Any]:
|
|
227
|
+
payload = {"input": kind}
|
|
228
|
+
payload.update({camelize(k): v for k, v in opts.items() if v is not None})
|
|
229
|
+
return payload
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def slider(
|
|
233
|
+
label: str,
|
|
234
|
+
as_: str,
|
|
235
|
+
min: Any = None,
|
|
236
|
+
max: Any = None,
|
|
237
|
+
step: Any = None,
|
|
238
|
+
value: Any = None,
|
|
239
|
+
**opts: Any,
|
|
240
|
+
) -> Dict[str, Any]:
|
|
241
|
+
return input(
|
|
242
|
+
"slider", label=label, as_=as_, min=min, max=max, step=step, value=value, **opts
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def select(
|
|
247
|
+
label: str,
|
|
248
|
+
as_: str,
|
|
249
|
+
options: Any,
|
|
250
|
+
multiple: bool = False,
|
|
251
|
+
value: Any = None,
|
|
252
|
+
**opts: Any,
|
|
253
|
+
) -> Dict[str, Any]:
|
|
254
|
+
return input(
|
|
255
|
+
"select",
|
|
256
|
+
label=label,
|
|
257
|
+
as_=as_,
|
|
258
|
+
options=options,
|
|
259
|
+
multiple=multiple,
|
|
260
|
+
value=value,
|
|
261
|
+
**opts,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def checkbox(label: str, as_: str, value: bool = False, **opts: Any) -> Dict[str, Any]:
|
|
266
|
+
return input("checkbox", label=label, as_=as_, value=value, **opts)
|
vgplot-0.1.0/api/spec.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
from .util import omit_none
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class Meta:
|
|
12
|
+
title: Optional[str] = None
|
|
13
|
+
description: Optional[str] = None
|
|
14
|
+
credit: Optional[str] = None
|
|
15
|
+
|
|
16
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
17
|
+
return omit_none(
|
|
18
|
+
{
|
|
19
|
+
"title": self.title,
|
|
20
|
+
"description": self.description,
|
|
21
|
+
"credit": self.credit,
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def meta(
|
|
27
|
+
title: str | None = None, description: str | None = None, credit: str | None = None
|
|
28
|
+
) -> Meta:
|
|
29
|
+
return Meta(title=title, description=description, credit=credit)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Spec:
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
*,
|
|
36
|
+
meta: Meta | Dict[str, Any] | None = None,
|
|
37
|
+
data: Dict[str, Any] | None = None,
|
|
38
|
+
params: Dict[str, Any] | None = None,
|
|
39
|
+
plotDefaults: Dict[str, Any] | None = None,
|
|
40
|
+
config: Dict[str, Any] | None = None,
|
|
41
|
+
view: Dict[str, Any] | None = None,
|
|
42
|
+
**extra: Any,
|
|
43
|
+
) -> None:
|
|
44
|
+
self.meta = meta.to_dict() if hasattr(meta, "to_dict") else meta
|
|
45
|
+
self.data = data
|
|
46
|
+
self.params = params
|
|
47
|
+
self.plotDefaults = plotDefaults
|
|
48
|
+
self.config = config
|
|
49
|
+
self.view = view or {}
|
|
50
|
+
self.extra = extra
|
|
51
|
+
|
|
52
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
53
|
+
base: Dict[str, Any] = {}
|
|
54
|
+
if self.meta:
|
|
55
|
+
base["meta"] = self.meta
|
|
56
|
+
if self.config:
|
|
57
|
+
base["config"] = self.config
|
|
58
|
+
if self.data:
|
|
59
|
+
base["data"] = self.data
|
|
60
|
+
if self.params:
|
|
61
|
+
base["params"] = self.params
|
|
62
|
+
if self.plotDefaults:
|
|
63
|
+
base["plotDefaults"] = self.plotDefaults
|
|
64
|
+
base.update(self.view)
|
|
65
|
+
base.update(omit_none(self.extra))
|
|
66
|
+
return base
|
|
67
|
+
|
|
68
|
+
def to_json(self, **kwargs: Any) -> str:
|
|
69
|
+
return json.dumps(self.to_dict(), **kwargs)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def spec(
|
|
73
|
+
*,
|
|
74
|
+
meta: Meta | Dict[str, Any] | None = None,
|
|
75
|
+
data: Dict[str, Any] | None = None,
|
|
76
|
+
params: Dict[str, Any] | None = None,
|
|
77
|
+
plotDefaults: Dict[str, Any] | None = None,
|
|
78
|
+
config: Dict[str, Any] | None = None,
|
|
79
|
+
view: Dict[str, Any] | None = None,
|
|
80
|
+
**extra: Any,
|
|
81
|
+
) -> Spec:
|
|
82
|
+
return Spec(
|
|
83
|
+
meta=meta,
|
|
84
|
+
data=data,
|
|
85
|
+
params=params,
|
|
86
|
+
plotDefaults=plotDefaults,
|
|
87
|
+
config=config,
|
|
88
|
+
view=view,
|
|
89
|
+
**extra,
|
|
90
|
+
)
|
vgplot-0.1.0/api/util.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def camelize(key: str) -> str:
|
|
5
|
+
"""Convert snake_case to lowerCamelCase."""
|
|
6
|
+
if not key:
|
|
7
|
+
return key
|
|
8
|
+
parts = key.split("_")
|
|
9
|
+
return parts[0] + "".join(p[:1].upper() + p[1:] for p in parts[1:])
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def omit_none(d: Dict[str, Any]) -> Dict[str, Any]:
|
|
13
|
+
"""Return a copy without None values."""
|
|
14
|
+
return {k: v for k, v in d.items() if v is not None}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "vgplot"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Schema wrapper classes for Mosaic"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.9"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"pyyaml",
|
|
9
|
+
"narwhals"
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[project.urls]
|
|
13
|
+
homepage = "https://github.com/uwdata/mosaic"
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
generate-schema-wrapper = "schema_wrapper.generate_schema_wrapper:main"
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["hatchling"]
|
|
20
|
+
build-backend = "hatchling.build"
|
|
21
|
+
|
|
22
|
+
[dependency-groups]
|
|
23
|
+
dev = [
|
|
24
|
+
"jupyterlab>=4.2.5",
|
|
25
|
+
"mypy>=1.11.1",
|
|
26
|
+
"pytest>=7.4.4",
|
|
27
|
+
"pytest-cov",
|
|
28
|
+
"ruff>=0.6.1",
|
|
29
|
+
"pandas",
|
|
30
|
+
"mosaic-widget"
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[tool.uv.sources]
|
|
34
|
+
"mosaic-widget" = { workspace = true }
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.targets.wheel]
|
|
37
|
+
packages = ["api", "schema_wrapper"]
|
|
38
|
+
|
|
39
|
+
# https://mypy.readthedocs.io/en/stable/config_file.html
|
|
40
|
+
[tool.mypy]
|
|
41
|
+
files = "src/**/*.py"
|
|
42
|
+
strict = false
|
|
43
|
+
ignore_missing_imports = true
|