Repository: altair-viz/altair

Issue:
Code generator uses `mapping(required=[])` in confusing ways
In particular:
```
>>> alt.Chart.transform_impute?
[...]
keyvals : anyOf(List(Mapping(required=[])), :class:`ImputeSequence`)
[...]
```
This should be something like
```
keyvals : anyOf(List(Any), :class:`ImputeSequence`)
```
Somehow the schema generator is treating an empty ``items`` schema as an object.

Comments:

Patch:
diff --git a/altair/vega/v5/schema/core.py b/altair/vega/v5/schema/core.py
index fd4aeb23..5e2a668d 100644
--- a/altair/vega/v5/schema/core.py
+++ b/altair/vega/v5/schema/core.py
@@ -774,7 +774,7 @@ class projection(VegaSchema):
 
     extent : oneOf(List(oneOf(List(:class:`numberOrSignal`), :class:`signal`)), :class:`signal`)
 
-    fit : oneOf(Mapping(required=[]), List(Mapping(required=[])))
+    fit : oneOf(Mapping(required=[]), List(Any))
 
     parallels : oneOf(List(:class:`numberOrSignal`), :class:`signal`)
 
@@ -868,7 +868,7 @@ class scope(VegaSchema):
 class signalName(VegaSchema):
     """signalName schema wrapper
 
-    not Mapping(required=[])
+    not enum('parent', 'datum', 'event', 'item')
     """
     _schema = {'$ref': '#/defs/signalName'}
     _rootschema = Root._schema
@@ -977,7 +977,7 @@ class crossfilterTransform(VegaSchema):
     fields : oneOf(List(oneOf(:class:`scaleField`, :class:`paramField`, :class:`expr`)),
     :class:`signal`)
 
-    query : oneOf(List(Mapping(required=[])), :class:`signal`)
+    query : oneOf(List(Any), :class:`signal`)
 
     type : enum('crossfilter')
 
@@ -1000,7 +1000,7 @@ class resolvefilterTransform(VegaSchema):
     Attributes
     ----------
 
-    filter : Mapping(required=[])
+    filter : Any
 
     ignore : anyOf(float, :class:`signal`)
 
@@ -1339,11 +1339,11 @@ class graticuleTransform(VegaSchema):
 
     type : enum('graticule')
 
-    extent : oneOf(List(Mapping(required=[])), :class:`signal`)
+    extent : oneOf(List(Any), :class:`signal`)
 
-    extentMajor : oneOf(List(Mapping(required=[])), :class:`signal`)
+    extentMajor : oneOf(List(Any), :class:`signal`)
 
-    extentMinor : oneOf(List(Mapping(required=[])), :class:`signal`)
+    extentMinor : oneOf(List(Any), :class:`signal`)
 
     precision : anyOf(float, :class:`signal`)
 
@@ -2182,13 +2182,13 @@ class imputeTransform(VegaSchema):
     groupby : oneOf(List(oneOf(:class:`scaleField`, :class:`paramField`, :class:`expr`)),
     :class:`signal`)
 
-    keyvals : oneOf(List(Mapping(required=[])), :class:`signal`)
+    keyvals : oneOf(List(Any), :class:`signal`)
 
     method : anyOf(enum('value', 'mean', 'median', 'max', 'min'), :class:`signal`)
 
     signal : string
 
-    value : Mapping(required=[])
+    value : Any
 
     """
     _schema = {'$ref': '#/defs/imputeTransform'}
@@ -2301,7 +2301,7 @@ class lookupTransform(VegaSchema):
 
     type : enum('lookup')
 
-    default : Mapping(required=[])
+    default : Any
 
     signal : string
 
@@ -2586,7 +2586,7 @@ class voronoiTransform(VegaSchema):
 
     y : oneOf(:class:`scaleField`, :class:`paramField`, :class:`expr`)
 
-    extent : oneOf(List(Mapping(required=[])), :class:`signal`)
+    extent : oneOf(List(Any), :class:`signal`)
 
     signal : string
 
@@ -3458,7 +3458,7 @@ class signal(VegaSchema):
 class arrayOrSignal(VegaSchema):
     """arrayOrSignal schema wrapper
 
-    oneOf(List(Mapping(required=[])), :class:`signal`)
+    oneOf(List(Any), :class:`signal`)
     """
     _schema = {'$ref': '#/refs/arrayOrSignal'}
     _rootschema = Root._schema
diff --git a/altair/vegalite/v3/schema/core.py b/altair/vegalite/v3/schema/core.py
index 52997ac1..14b19534 100644
--- a/altair/vegalite/v3/schema/core.py
+++ b/altair/vegalite/v3/schema/core.py
@@ -1321,7 +1321,7 @@ class BaseMarkConfig(VegaLiteSchema):
         the ``x`` and ``y`` properties. Values for ``theta`` follow the same convention of
         ``arc`` mark ``startAngle`` and ``endAngle`` properties: angles are measured in
         radians, with ``0`` indicating "north".
-    tooltip : Mapping(required=[])
+    tooltip : Any
         The tooltip text to show upon mouse hover.
     width : float
         Width of the marks.
@@ -1550,7 +1550,7 @@ class BindRadioSelect(Binding):
 
     input : enum('radio', 'select')
 
-    options : List(Mapping(required=[]))
+    options : List(Any)
 
     debounce : float
 
@@ -4059,13 +4059,13 @@ class ErrorBarExtent(VegaLiteSchema):
 class EventStream(VegaLiteSchema):
     """EventStream schema wrapper
 
-    Mapping(required=[])
+    Any
     """
     _schema = {'$ref': '#/definitions/EventStream'}
     _rootschema = Root._schema
 
-    def __init__(self, **kwds):
-        super(EventStream, self).__init__(**kwds)
+    def __init__(self, *args, **kwds):
+        super(EventStream, self).__init__(*args, **kwds)
 
 
 class FacetFieldDef(VegaLiteSchema):
@@ -5927,7 +5927,7 @@ class ImputeParams(VegaLiteSchema):
 
         **Default value:** :  ``[null, null]`` indicating that the window includes all
         objects.
-    keyvals : anyOf(List(Mapping(required=[])), :class:`ImputeSequence`)
+    keyvals : anyOf(List(Any), :class:`ImputeSequence`)
         Defines the key values that should be considered for imputation.
         An array of key values or an object defining a `number sequence
         <https://vega.github.io/vega-lite/docs/impute.html#sequence-def>`__.
@@ -5943,7 +5943,7 @@ class ImputeParams(VegaLiteSchema):
         One of ``value``, ``mean``, ``median``, ``max`` or ``min``.
 
         **Default value:**  ``"value"``
-    value : Mapping(required=[])
+    value : Any
         The field value to use when the imputation ``method`` is ``"value"``.
     """
     _schema = {'$ref': '#/definitions/ImputeParams'}
@@ -15392,7 +15392,7 @@ class ImputeTransform(Transform):
     groupby : List(:class:`FieldName`)
         An optional array of fields by which to group the values.
         Imputation will then be performed on a per-group basis.
-    keyvals : anyOf(List(Mapping(required=[])), :class:`ImputeSequence`)
+    keyvals : anyOf(List(Any), :class:`ImputeSequence`)
         Defines the key values that should be considered for imputation.
         An array of key values or an object defining a `number sequence
         <https://vega.github.io/vega-lite/docs/impute.html#sequence-def>`__.
@@ -15408,7 +15408,7 @@ class ImputeTransform(Transform):
         One of ``value``, ``mean``, ``median``, ``max`` or ``min``.
 
         **Default value:**  ``"value"``
-    value : Mapping(required=[])
+    value : Any
         The field value to use when the imputation ``method`` is ``"value"``.
     """
     _schema = {'$ref': '#/definitions/ImputeTransform'}
diff --git a/altair/vegalite/v4/schema/core.py b/altair/vegalite/v4/schema/core.py
index 929815af..addb0b93 100644
--- a/altair/vegalite/v4/schema/core.py
+++ b/altair/vegalite/v4/schema/core.py
@@ -1414,7 +1414,7 @@ class BaseMarkConfig(VegaLiteSchema):
         the ``x`` and ``y`` properties. Values for ``theta`` follow the same convention of
         ``arc`` mark ``startAngle`` and ``endAngle`` properties: angles are measured in
         radians, with ``0`` indicating "north".
-    tooltip : Mapping(required=[])
+    tooltip : Any
         The tooltip text to show upon mouse hover.
     width : float
         Width of the marks.
@@ -1693,7 +1693,7 @@ class BindRadioSelect(Binding):
 
     input : enum('radio', 'select')
 
-    options : List(Mapping(required=[]))
+    options : List(Any)
 
     debounce : float
 
@@ -7557,7 +7557,7 @@ class ImputeParams(VegaLiteSchema):
 
         **Default value:** :  ``[null, null]`` indicating that the window includes all
         objects.
-    keyvals : anyOf(List(Mapping(required=[])), :class:`ImputeSequence`)
+    keyvals : anyOf(List(Any), :class:`ImputeSequence`)
         Defines the key values that should be considered for imputation.
         An array of key values or an object defining a `number sequence
         <https://vega.github.io/vega-lite/docs/impute.html#sequence-def>`__.
@@ -7573,7 +7573,7 @@ class ImputeParams(VegaLiteSchema):
         One of ``"value"``, ``"mean"``, ``"median"``, ``"max"`` or ``"min"``.
 
         **Default value:**  ``"value"``
-    value : Mapping(required=[])
+    value : Any
         The field value to use when the imputation ``method`` is ``"value"``.
     """
     _schema = {'$ref': '#/definitions/ImputeParams'}
@@ -17546,7 +17546,7 @@ class ImputeTransform(Transform):
     groupby : List(:class:`FieldName`)
         An optional array of fields by which to group the values.
         Imputation will then be performed on a per-group basis.
-    keyvals : anyOf(List(Mapping(required=[])), :class:`ImputeSequence`)
+    keyvals : anyOf(List(Any), :class:`ImputeSequence`)
         Defines the key values that should be considered for imputation.
         An array of key values or an object defining a `number sequence
         <https://vega.github.io/vega-lite/docs/impute.html#sequence-def>`__.
@@ -17562,7 +17562,7 @@ class ImputeTransform(Transform):
         One of ``"value"``, ``"mean"``, ``"median"``, ``"max"`` or ``"min"``.
 
         **Default value:**  ``"value"``
-    value : Mapping(required=[])
+    value : Any
         The field value to use when the imputation ``method`` is ``"value"``.
     """
     _schema = {'$ref': '#/definitions/ImputeTransform'}
diff --git a/tools/schemapi/utils.py b/tools/schemapi/utils.py
index c367db92..d976ff4f 100644
--- a/tools/schemapi/utils.py
+++ b/tools/schemapi/utils.py
@@ -188,7 +188,7 @@ class SchemaInfo(object):
             return '[{0}]'.format(', '.join(self.child(s).short_description
                                             for s in self.schema))
         elif self.is_empty():
-            return 'any object'
+            return 'Any'
         elif self.is_enum():
             return 'enum({})'.format(', '.join(map(repr, self.enum)))
         elif self.is_anyOf():
@@ -266,7 +266,7 @@ class SchemaInfo(object):
 
     @property
     def not_(self):
-        return self.child(self.schema.get('not_', {}))
+        return self.child(self.schema.get('not', {}))
 
     @property
     def items(self):
@@ -299,7 +299,7 @@ class SchemaInfo(object):
         return 'enum' in self.schema
 
     def is_empty(self):
-        return set(self.schema.keys()) - set(EXCLUDE_KEYS) == {}
+        return not (set(self.schema.keys()) - set(EXCLUDE_KEYS))
 
     def is_compound(self):
         return any(key in self.schema for key in ['anyOf', 'allOf', 'oneOf'])


Test patch:
diff --git a/tools/schemapi/tests/test_utils.py b/tools/schemapi/tests/test_utils.py
index f168b57b..769042f7 100644
--- a/tools/schemapi/tests/test_utils.py
+++ b/tools/schemapi/tests/test_utils.py
@@ -1,6 +1,6 @@
 import pytest
 
-from ..utils import get_valid_identifier
+from ..utils import get_valid_identifier, SchemaInfo
 from ..schemapi import _FromDict
 
 
@@ -31,3 +31,16 @@ def test_hash_schema(refschema, use_json):
     copy['description'] = "A schema"
     copy['title'] = "Schema to test"
     assert _FromDict.hash_schema(refschema) == _FromDict.hash_schema(copy)
+
+@pytest.mark.parametrize('schema, expected', [
+    ({}, 'Any'),
+    ({'type': 'number'}, 'float'),
+    ({'enum': ['A', 'B', 'C']}, "enum('A', 'B', 'C')"),
+    ({'type': 'array'}, 'List(Any)'),
+    ({'type': 'object'}, 'Mapping(required=[])'),
+    ({"type": "string", "not": {'enum': ['A', 'B', 'C']}}, "not enum('A', 'B', 'C')"),
+])
+def test_medium_description(schema, expected):
+    description = SchemaInfo(schema).medium_description
+    assert description == expected
+