Skip to content

Commit

Permalink
Backport bug fixes for a 4.2.1 release (#2827)
Browse files Browse the repository at this point in the history
* DOC: remove unused section

* Disable uri-reference format check in jsonsschema (#2771)

* Disable uri-reference format check. Consistently use same validator across codebase

* Remove validation in SchemaInfo as not used anywhere and it referenced the wrong jsonschema draft

* Add compatibility for older jsonschema versions

* Improve comments

* Simplify validate_jsonschema

* Replace `iteritems` with `items` due to pandas deprecation (#2683)

* Add changelog entry.

* Bump version.

* Run black and flake8.

* Pin selenium in CI.

Co-authored-by: Jake VanderPlas <jakevdp@google.com>
Co-authored-by: Stefan Binder <binder_stefan@outlook.com>
Co-authored-by: Joel Ostblom <joelostblom@users.noreply.github.com>
  • Loading branch information
4 people committed Jan 20, 2023
1 parent f654bbe commit 6d214f9
Show file tree
Hide file tree
Showing 12 changed files with 73 additions and 192 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Expand Up @@ -24,6 +24,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install .[dev]
pip install "selenium<4.3.0"
pip install altair_saver
- name: Test with pytest
run: |
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/docbuild.yml
Expand Up @@ -19,10 +19,9 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install .[dev]
pip install "selenium<4.3.0"
pip install altair_saver
pip install -r doc/requirements.txt
- name: Run docbuild
run: |
cd doc && make ${{ matrix.build-type }}
2 changes: 1 addition & 1 deletion altair/__init__.py
@@ -1,5 +1,5 @@
# flake8: noqa
__version__ = "4.2.0"
__version__ = "4.2.1"

from .vegalite import *
from . import examples
Expand Down
2 changes: 1 addition & 1 deletion altair/utils/core.py
Expand Up @@ -314,7 +314,7 @@ def to_list_if_array(val):
else:
return val

for col_name, dtype in df.dtypes.iteritems():
for col_name, dtype in df.dtypes.items():
if str(dtype) == "category":
# XXXX: work around bug in to_json for categorical types
# https://github.com/pydata/pandas/issues/10778
Expand Down
5 changes: 2 additions & 3 deletions altair/utils/display.py
Expand Up @@ -4,10 +4,9 @@
from typing import Callable, Dict
import uuid

from jsonschema import validate

from .plugin_registry import PluginRegistry
from .mimebundle import spec_to_mimebundle
from .schemapi import validate_jsonschema


# ==============================================================================
Expand Down Expand Up @@ -126,7 +125,7 @@ def _validate(self):
# type: () -> None
"""Validate the spec against the schema."""
schema_dict = json.loads(pkgutil.get_data(*self.schema_path).decode("utf-8"))
validate(self.spec, schema_dict)
validate_jsonschema(self.spec, schema_dict)

def _repr_mimebundle_(self, include=None, exclude=None):
"""Return a MIME bundle for display in Jupyter frontends."""
Expand Down
31 changes: 24 additions & 7 deletions altair/utils/schemapi.py
Expand Up @@ -6,10 +6,11 @@
import json

import jsonschema
import jsonschema.exceptions
import numpy as np
import pandas as pd


JSONSCHEMA_VALIDATOR = jsonschema.Draft7Validator
# If DEBUG_MODE is True, then schema objects are converted to dict and
# validated at creation time. This slows things down, particularly for
# larger specs, but leads to much more useful tracebacks for the user.
Expand Down Expand Up @@ -39,6 +40,24 @@ def debug_mode(arg):
DEBUG_MODE = original


def validate_jsonschema(spec, schema, resolver=None):
# We don't use jsonschema.validate as this would validate the schema itself.
# Instead, we pass the schema directly to the validator class. This is done for
# two reasons: The schema comes from Vega-Lite and is not based on the user
# input, therefore there is no need to validate it in the first place. Furthermore,
# the "uri-reference" format checker fails for some of the references as URIs in
# "$ref" are not encoded,
# e.g. '#/definitions/ValueDefWithCondition<MarkPropFieldOrDatumDef,
# (Gradient|string|null)>' would be a valid $ref in a Vega-Lite schema but
# it is not a valid URI reference due to the characters such as '<'.
validator = JSONSCHEMA_VALIDATOR(
schema, format_checker=JSONSCHEMA_VALIDATOR.FORMAT_CHECKER, resolver=resolver
)
error = jsonschema.exceptions.best_match(validator.iter_errors(spec))
if error is not None:
raise error


def _subclasses(cls):
"""Breadth-first sequence of all classes which inherit from cls."""
seen = set()
Expand Down Expand Up @@ -150,7 +169,7 @@ class SchemaBase(object):
_schema = None
_rootschema = None
_class_is_valid_at_instantiation = True
_validator = jsonschema.Draft7Validator
_validator = JSONSCHEMA_VALIDATOR

def __init__(self, *args, **kwds):
# Two valid options for initialization, which should be handled by
Expand Down Expand Up @@ -440,9 +459,7 @@ def validate(cls, instance, schema=None):
if schema is None:
schema = cls._schema
resolver = jsonschema.RefResolver.from_schema(cls._rootschema or cls._schema)
return jsonschema.validate(
instance, schema, cls=cls._validator, resolver=resolver
)
return validate_jsonschema(instance, schema, resolver=resolver)

@classmethod
def resolve_references(cls, schema=None):
Expand All @@ -461,7 +478,7 @@ def validate_property(cls, name, value, schema=None):
value = _todict(value, validate=False, context={})
props = cls.resolve_references(schema or cls._schema).get("properties", {})
resolver = jsonschema.RefResolver.from_schema(cls._rootschema or cls._schema)
return jsonschema.validate(value, props.get(name, {}), resolver=resolver)
return validate_jsonschema(value, props.get(name, {}), resolver=resolver)

def __dir__(self):
return list(self._kwds.keys())
Expand Down Expand Up @@ -555,7 +572,7 @@ def from_dict(
for possible_schema in schemas:
resolver = jsonschema.RefResolver.from_schema(rootschema)
try:
jsonschema.validate(dct, possible_schema, resolver=resolver)
validate_jsonschema(dct, possible_schema, resolver=resolver)
except jsonschema.ValidationError:
continue
else:
Expand Down
6 changes: 3 additions & 3 deletions altair/utils/tests/test_plugin_registry.py
Expand Up @@ -26,7 +26,7 @@ def test_plugin_registry():
assert plugins.get() is None
assert repr(plugins) == "TypedCallableRegistry(active='', registered=[])"

plugins.register("new_plugin", lambda x: x ** 2)
plugins.register("new_plugin", lambda x: x**2)
assert plugins.names() == ["new_plugin"]
assert plugins.active == ""
assert plugins.get() is None
Expand All @@ -46,7 +46,7 @@ def test_plugin_registry():
def test_plugin_registry_extra_options():
plugins = GeneralCallableRegistry()

plugins.register("metadata_plugin", lambda x, p=2: x ** p)
plugins.register("metadata_plugin", lambda x, p=2: x**p)
plugins.enable("metadata_plugin")
assert plugins.get()(3) == 9

Expand Down Expand Up @@ -86,7 +86,7 @@ def test_plugin_registry_global_settings():
def test_plugin_registry_context():
plugins = GeneralCallableRegistry()

plugins.register("default", lambda x, p=2: x ** p)
plugins.register("default", lambda x, p=2: x**p)

# At first there is no plugin enabled
assert plugins.active == ""
Expand Down
12 changes: 6 additions & 6 deletions altair/utils/tests/test_utils.py
Expand Up @@ -129,9 +129,9 @@ def test_sanitize_nullable_integers():
)

df_clean = sanitize_dataframe(df)
assert {col.dtype.name for _, col in df_clean.iteritems()} == {"object"}
assert {col.dtype.name for _, col in df_clean.items()} == {"object"}

result_python = {col_name: list(col) for col_name, col in df_clean.iteritems()}
result_python = {col_name: list(col) for col_name, col in df_clean.items()}
assert result_python == {
"int_np": [1, 2, 3, 4, 5],
"int64": [1, 2, 3, None, 5],
Expand All @@ -157,9 +157,9 @@ def test_sanitize_string_dtype():
)

df_clean = sanitize_dataframe(df)
assert {col.dtype.name for _, col in df_clean.iteritems()} == {"object"}
assert {col.dtype.name for _, col in df_clean.items()} == {"object"}

result_python = {col_name: list(col) for col_name, col in df_clean.iteritems()}
result_python = {col_name: list(col) for col_name, col in df_clean.items()}
assert result_python == {
"string_object": ["a", "b", "c", "d"],
"string_string": ["a", "b", "c", "d"],
Expand All @@ -182,9 +182,9 @@ def test_sanitize_boolean_dtype():
)

df_clean = sanitize_dataframe(df)
assert {col.dtype.name for _, col in df_clean.iteritems()} == {"object"}
assert {col.dtype.name for _, col in df_clean.items()} == {"object"}

result_python = {col_name: list(col) for col_name, col in df_clean.iteritems()}
result_python = {col_name: list(col) for col_name, col in df_clean.items()}
assert result_python == {
"bool_none": [True, False, None],
"none": [None, None, None],
Expand Down
9 changes: 9 additions & 0 deletions doc/releases/changes.rst
Expand Up @@ -3,6 +3,15 @@
Altair Change Log
=================

Version 4.2.1 (released XXX XX, 2023)
-------------------------------------

Bug Fixes
~~~~~~~~~
- Disable uri-reference format check in jsonsschema (#2771)
- Replace ``iteritems`` with ```items``` due to pandas deprecation (#2683)


Version 4.2.0 (released Dec 29, 2021)
-------------------------------------
- Update Vega-Lite from version 4.8.1 to version 4.17.0;
Expand Down
149 changes: 0 additions & 149 deletions tools/schemapi/jsonschema-draft04.json

This file was deleted.

0 comments on commit 6d214f9

Please sign in to comment.