Repository: vega/altair

Issue:
Clean up and simplify tools folder
I track some cleanup tasks to the code generation below which we could tackle after the release of version 5:

* `tools/schemapi/tests/test_schemapi.py` is the only test file that is copied into `tests`. All other test files are maintaned directly in the `tests` folder -> Maintain directly in `tests` as well
* `tools/schemapi/tests/test_decorator.py` and `tools/schemapi/tests/test_utils.py` are not copied -> Evaluate if can be removed or if they should also be maintained in `tests`
* `tools/schemapi/decorator.py` is not used anywhere -> Remove?
* ...


Discussion:

Resolving patch:
diff --git a/tools/generate_schema_wrapper.py b/tools/generate_schema_wrapper.py
--- a/tools/generate_schema_wrapper.py
+++ b/tools/generate_schema_wrapper.py
@@ -294,20 +294,6 @@ def copy_schemapi_util():
             dest.write(HEADER)
             dest.writelines(source.readlines())
 
-    # Copy the schemapi test file
-    source_path = abspath(
-        join(dirname(__file__), "schemapi", "tests", "test_schemapi.py")
-    )
-    destination_path = abspath(
-        join(dirname(__file__), "..", "tests", "utils", "tests", "test_schemapi.py")
-    )
-
-    print("Copying\n {}\n  -> {}".format(source_path, destination_path))
-    with open(source_path, "r", encoding="utf8") as source:
-        with open(destination_path, "w", encoding="utf8") as dest:
-            dest.write(HEADER)
-            dest.writelines(source.readlines())
-
 
 def recursive_dict_update(schema, root, def_dict):
     if "$ref" in schema:
diff --git a/tools/schemapi/__init__.py b/tools/schemapi/__init__.py
--- a/tools/schemapi/__init__.py
+++ b/tools/schemapi/__init__.py
@@ -2,8 +2,7 @@
 schemapi: tools for generating Python APIs from JSON schemas
 """
 from .schemapi import SchemaBase, Undefined
-from .decorator import schemaclass
 from .utils import SchemaInfo
 
 
-__all__ = ("SchemaBase", "Undefined", "schemaclass", "SchemaInfo")
+__all__ = ("SchemaBase", "Undefined", "SchemaInfo")
diff --git a/tools/schemapi/decorator.py b/tools/schemapi/decorator.py
deleted file mode 100644
--- a/tools/schemapi/decorator.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import warnings
-from . import codegen, SchemaBase, Undefined
-
-
-def schemaclass(*args, init_func=True, docstring=True, property_map=True):
-    """A decorator to add boilerplate to a schema class
-
-    This will read the _json_schema attribute of a SchemaBase class, and add
-    one or all of three attributes/methods, based on the schema:
-
-    - An __init__ function
-    - a __doc__ docstring
-
-    In all cases, if the attribute/method is explicitly defined in the class
-    it will not be overwritten.
-
-    A simple invocation adds all three attributes/methods:
-
-        @schemaclass
-        class MySchema(SchemaBase):
-            __schema = {...}
-
-    Optionally, you can invoke schemaclass with arguments to turn off
-    some of the added behaviors:
-
-        @schemaclass(init_func=True, docstring=False)
-        class MySchema(SchemaBase):
-            __schema = {...}
-    """
-
-    def _decorator(cls, init_func=init_func, docstring=docstring):
-        if not (isinstance(cls, type) and issubclass(cls, SchemaBase)):
-            warnings.warn("class is not an instance of SchemaBase.")
-
-        name = cls.__name__
-        gen = codegen.SchemaGenerator(
-            name, schema=cls._schema, rootschema=cls._rootschema
-        )
-
-        if init_func and "__init__" not in cls.__dict__:
-            init_code = gen.init_code()
-            globals_ = {name: cls, "Undefined": Undefined}
-            locals_ = {}
-            exec(init_code, globals_, locals_)
-            setattr(cls, "__init__", locals_["__init__"])
-
-        if docstring and not cls.__doc__:
-            setattr(cls, "__doc__", gen.docstring())
-        return cls
-
-    if len(args) == 0:
-        return _decorator
-    elif len(args) == 1:
-        return _decorator(args[0])
-    else:
-        raise ValueError(
-            "optional arguments to schemaclass must be " "passed by keyword"
-        )


Test patch:
diff --git a/tests/utils/tests/test_schemapi.py b/tests/utils/tests/test_schemapi.py
--- a/tests/utils/tests/test_schemapi.py
+++ b/tests/utils/tests/test_schemapi.py
@@ -1,5 +1,3 @@
-# The contents of this file are automatically written by
-# tools/generate_schema_wrapper.py. Do not modify directly.
 import copy
 import io
 import inspect
diff --git a/tools/schemapi/tests/test_decorator.py b/tools/schemapi/tests/test_decorator.py
deleted file mode 100644
--- a/tools/schemapi/tests/test_decorator.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import inspect
-
-from .. import SchemaBase, Undefined, schemaclass
-
-
-@schemaclass
-class MySchema(SchemaBase):
-    _schema = {
-        "definitions": {
-            "StringMapping": {
-                "type": "object",
-                "additionalProperties": {"type": "string"},
-            },
-            "StringArray": {"type": "array", "items": {"type": "string"}},
-        },
-        "properties": {
-            "a": {"$ref": "#/definitions/StringMapping"},
-            "a2": {"type": "object", "additionalProperties": {"type": "number"}},
-            "b": {"$ref": "#/definitions/StringArray"},
-            "b2": {"type": "array", "items": {"type": "number"}},
-            "c": {"type": ["string", "number"]},
-            "d": {
-                "anyOf": [
-                    {"$ref": "#/definitions/StringMapping"},
-                    {"$ref": "#/definitions/StringArray"},
-                ]
-            },
-        },
-        "required": ["a", "b"],
-    }
-
-
-@schemaclass
-class StringArray(SchemaBase):
-    _schema = MySchema._schema["definitions"]["StringArray"]
-    _rootschema = MySchema._schema
-
-
-def test_myschema_decorator():
-    myschema = MySchema({"foo": "bar"}, ["foo", "bar"])
-    assert myschema.to_dict() == {"a": {"foo": "bar"}, "b": ["foo", "bar"]}
-
-    myschema = MySchema.from_dict({"a": {"foo": "bar"}, "b": ["foo", "bar"]})
-    assert myschema.to_dict() == {"a": {"foo": "bar"}, "b": ["foo", "bar"]}
-
-    assert MySchema.__doc__.startswith("MySchema schema wrapper")
-    argspec = inspect.getfullargspec(MySchema.__init__)
-    assert argspec.args == ["self", "a", "b", "a2", "b2", "c", "d"]
-    assert argspec.defaults == 6 * (Undefined,)
-    assert argspec.varargs is None
-    assert argspec.varkw == "kwds"
-
-
-def test_stringarray_decorator():
-    arr = StringArray(["a", "b", "c"])
-    assert arr.to_dict() == ["a", "b", "c"]
-
-    arr = StringArray.from_dict(["a", "b", "c"])
-    assert arr.to_dict() == ["a", "b", "c"]
-
-    assert arr.__doc__.startswith("StringArray schema wrapper")
-    argspec = inspect.getfullargspec(StringArray.__init__)
-    assert argspec.args == ["self"]
-    assert argspec.varargs == "args"
-    assert argspec.varkw is None
diff --git a/tools/schemapi/tests/test_utils.py b/tools/schemapi/tests/test_utils.py
deleted file mode 100644
--- a/tools/schemapi/tests/test_utils.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import pytest
-
-from ..utils import get_valid_identifier, SchemaInfo
-from ..schemapi import _FromDict
-
-
-@pytest.fixture
-def refschema():
-    return {
-        "$ref": "#/definitions/Foo",
-        "definitions": {
-            "Foo": {"$ref": "#/definitions/Bar"},
-            "Bar": {"$ref": "#/definitions/Baz"},
-            "Baz": {"type": "string"},
-        },
-    }
-
-
-def test_get_valid_identifier():
-    assert get_valid_identifier("$schema") == "schema"
-    assert get_valid_identifier("$ref") == "ref"
-    assert get_valid_identifier("foo-bar") == "foobar"
-    assert get_valid_identifier("$as") == "as_"
-    assert get_valid_identifier("for") == "for_"
-    assert get_valid_identifier("--") == "_"
-
-
-@pytest.mark.parametrize("use_json", [True, False])
-def test_hash_schema(refschema, use_json):
-    copy = refschema.copy()
-    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