JSON schema generation incomplete
- Dominant language
- Python
- Stars
- 521
- Forks
- 86
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 35
Description
At the moment, Param's JSON schema support is meant to work like this:
```python
import param, json, jsonschema
class P(param.Parameterized):
a = param.Integer(default=5, doc='Int', bounds=(2,30), inclusive_bounds=(True, False))
b = param.Number(default=4.3, allow_None=True)
c = param.String(default='foo')
p = P(a=29)
s = p.param.serialize_parameters()
d = json.loads(s)
full_schema = {"type" : "object", "properties" : p.param.schema()}
jsonschema.validate(instance=d, schema=full_schema)
```
I.e., `jsonschema.validate` accepts a python dictionary (the output of `json.loads(s)` on a string `s`) and a "full" schema (with "object" at the top level). This works, as you can see if you change one of the dictionary entries to something not matching the schema:
```python
d2 = d.copy()
d2.update(dict(a='astring'))
with param.exceptions_summarized():
jsonschema.validate(instance=d2, schema=full_schema)
```
```bash
ValidationError: 'astring' is not of type 'integer'
```
However, the validation also "succeeds" in many other cases that one would think would be validation errors:
```python
jsonschema.validate(instance=d2, schema=p.param.schema())
jsonschema.validate(instance=s, schema=p.param.schema())
jsonschema.validate(instance=42, schema=p.param.schema())
jsonschema.validate(instance="garbage", schema=p.param.schema())
```
I.e., if you pass not a full schema but the partial schema produced by `p.param.schema()`, `jsonschema.validate` will happily accept `d2`, or `s`, or pretty much anything you supply (e.g. the string "garbage" or an integer). All of these are rejected correctly if you pass `full_schema`.
Thus things only work properly when you pass `full_schema`, and worse, without `full_schema` they fail silently, which is likely to mislead users into thinking their input is valid when it is not.
Given the awkwardness of having to construct `full_schema` by hand, and the misleading output when using anything else, one might reasonably ask why `p.param.schema()` is not simply generating `full_schema` all the time, both for convenience and to ensure good validation in cases where the programmer is (reasonably) confused.
According to @jlstevens, he was reluctant to generate a `full_schema` directly because such an object represents a [JavaScript "object" type](https://www.w3schools.com/js/js_objects.asp), roughly corresponding to a Python dictionary, whereas here we are representing a Python class instance, which _has_ a dictionary but is not identical to a dictionary. Still, given the clear need to represent full Parameterized objects in JSON (see #520), plus the inconvenience of pasting on the `full_schema` boilerplate for the schema to be used with `jsonschema.validate`, plus the highly misleading behavior when supplying anything less than the `full_schema`, I strongly vote for `p.param.schema()` to return the equivalent of `full_schema` directly in all cases.
Contributor guide
Assessment
This issue has not been assessed yet.