Skip to content

Type with array of integer and number should test for integer before number #1318

Description

@raspe88

Consider you have the following schema being "integer" or "number"

{
  "id": "http://json-schema.org/geo",
  "$schema": "http://json-schema.org/draft-06/schema#",
  "description": "A geographical coordinate",
  "type": ["integer", "number"]
}

then - at least when generating code for Python 3.7 - the generated code will be

from typing import Union, Any


def from_float(x: Any) -> float:
    assert isinstance(x, (float, int)) and not isinstance(x, bool)
    return float(x)


def from_int(x: Any) -> int:
    assert isinstance(x, int) and not isinstance(x, bool)
    return x


def from_union(fs, x):
    for f in fs:
        try:
            return f(x)
        except:
            pass
    assert False


def to_float(x: Any) -> float:
    assert isinstance(x, float)
    return x


def coordinate_from_dict(s: Any) -> Union[float, int]:
    return from_union([from_float, from_int], s)


def coordinate_to_dict(x: Union[float, int]) -> Any:
    return from_union([to_float, from_int], x)

As you can see, in coordinate_from_dict the function from_int will not have any effect because it is placed behind from_float and that one already handles floats and integers. While this basically seems to be acceptable on the first glance because number is the supertype of integer, I consider this a problem because:

  1. Messages can grow by an additional factor of 3: Imagine you are sending a large array of integers and numbers from the range [0-9.99] but due to some reason right now you just need numbers. Each integer, e.g. 1, will become a number, e.g. 1.0, which has a length of 300% per number compared to an encoding without decimal point.
  2. No bijective functions: Users should be able to expect that coordinate_to_dict(coordinate_from_dict(some_input)) results in an identical dictionary when comparing to the original input.
  3. List entry without effect: The from_int within from_union([from_float, from_int], s) is useless.
  4. More code generated than needed: An additional function is needed (from_float and to_float).
  5. Inconsistency to other code generation: It is inconsistent to other code generation where datatypes are more relevant than in Python. e.g. in C++ it is
inline void from_json(const json & j, boost::variant<double, int64_t> & x) {
        if (j.is_number_integer())
            x = j.get<int64_t>();
        else if (j.is_number())
            x = j.get<double>();
        else throw "Could not deserialize";
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions