Пример #1
0
 def test_compare(self):
     x = msgspec.Ext(1, b"two")
     x2 = msgspec.Ext(1, b"two")
     x3 = msgspec.Ext(1, b"three")
     x4 = msgspec.Ext(2, b"two")
     assert x == x2
     assert not (x != x2)
     assert x != x3
     assert not (x == x3)
     assert x != x4
     assert not (x == x4)
Пример #2
0
    def test_roundtrip_typed_decoder(self, size):
        dec = msgspec.Decoder(msgspec.Ext)

        ext = msgspec.Ext(5, b"x" * size)
        buf = msgspec.encode(ext)
        out = dec.decode(buf)
        assert out == ext
Пример #3
0
    def test_roundtrip(self, size):
        data = b"x" * size
        code = 5

        buf = msgspec.encode(msgspec.Ext(code, data))
        out = msgspec.decode(buf)
        assert out.code == code
        assert out.data == data
Пример #4
0
    def test_serialize_compatibility(self, size):
        msgpack = pytest.importorskip("msgpack")
        data = b"x" * size
        code = 5

        msgspec_bytes = msgspec.encode(msgspec.Ext(code, data))
        msgpack_bytes = msgpack.dumps(msgpack.ExtType(code, data))
        assert msgspec_bytes == msgpack_bytes
Пример #5
0
    def test_typed_decoder_skips_ext_hook(self):
        def ext_hook(code, data):
            assert False, "shouldn't ever get called"

        msg = [None, msgspec.Ext(1, b"test")]
        dec = msgspec.Decoder(List[Optional[msgspec.Ext]])
        buf = msgspec.encode(msg)
        out = dec.decode(buf)
        assert out == msg
Пример #6
0
    def test_decoder_ext_hook_raises(self):
        class CustomError(Exception):
            pass

        def ext_hook(code, buf):
            raise CustomError

        msg = msgspec.encode(range(5),
                             default=lambda x: msgspec.Ext(1, b"test"))
        with pytest.raises(CustomError):
            msgspec.decode(msg, ext_hook=ext_hook)
Пример #7
0
 def default(x):
     return msgspec.Ext(5, pickle.dumps(x))
Пример #8
0
 def test_serialize_other_types(self, typ):
     buf = b"test"
     a = msgspec.encode(msgspec.Ext(1, buf))
     b = msgspec.encode(msgspec.Ext(1, typ(buf)))
     assert a == b
Пример #9
0
class TestTypedDecoder:
    def check_unexpected_type(self, dec_type, val, msg):
        dec = msgspec.Decoder(dec_type)
        s = msgspec.Encoder().encode(val)
        with pytest.raises(msgspec.DecodingError, match=msg):
            dec.decode(s)

    def test_none(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(None)
        assert dec.decode(enc.encode(None)) is None
        with pytest.raises(msgspec.DecodingError, match="expected `None`"):
            assert dec.decode(enc.encode(1))

    @pytest.mark.parametrize("x", [False, True])
    def test_bool(self, x):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(bool)
        assert dec.decode(enc.encode(x)) is x

    def test_bool_unexpected_type(self):
        self.check_unexpected_type(bool, "a", "expected `bool`")

    @pytest.mark.parametrize("x", INTS)
    def test_int(self, x):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(int)
        assert dec.decode(enc.encode(x)) == x

    def test_int_unexpected_type(self):
        self.check_unexpected_type(int, "a", "expected `int`")

    @pytest.mark.parametrize("x", FLOATS + INTS)
    def test_float(self, x):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(float)
        res = dec.decode(enc.encode(x))
        sol = float(x)
        if math.isnan(sol):
            assert math.isnan(res)
        else:
            assert res == sol

    def test_float_unexpected_type(self):
        self.check_unexpected_type(float, "a", "expected `float`")

    @pytest.mark.parametrize("size", SIZES)
    def test_str(self, size):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(str)
        x = "a" * size
        res = dec.decode(enc.encode(x))
        assert res == x

    def test_str_unexpected_type(self):
        self.check_unexpected_type(str, 1, "expected `str`")

    @pytest.mark.parametrize("size", SIZES)
    def test_bytes(self, size):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(bytes)
        x = b"a" * size
        res = dec.decode(enc.encode(x))
        assert isinstance(res, bytes)
        assert res == x

    def test_bytes_unexpected_type(self):
        self.check_unexpected_type(bytes, 1, "expected `bytes`")

    @pytest.mark.parametrize("size", SIZES)
    def test_bytearray(self, size):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(bytearray)
        x = bytearray(size)
        res = dec.decode(enc.encode(x))
        assert isinstance(res, bytearray)
        assert res == x

    def test_bytearray_unexpected_type(self):
        self.check_unexpected_type(bytearray, 1, "expected `bytearray`")

    @pytest.mark.parametrize("size", SIZES)
    def test_list_lengths(self, size):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(list)
        x = list(range(size))
        res = dec.decode(enc.encode(x))
        assert res == x

    @pytest.mark.parametrize("typ", [list, List, List[Any]])
    def test_list_any(self, typ):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(typ)
        x = [1, "two", b"three"]
        res = dec.decode(enc.encode(x))
        assert res == x
        with pytest.raises(msgspec.DecodingError, match="expected `list`"):
            dec.decode(enc.encode(1))

    def test_list_typed(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(List[int])
        x = [1, 2, 3]
        res = dec.decode(enc.encode(x))
        assert res == x
        with pytest.raises(msgspec.DecodingError, match="expected `int`"):
            dec.decode(enc.encode([1, 2, "three"]))

    @pytest.mark.parametrize("size", SIZES)
    def test_set_lengths(self, size):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(set)
        x = set(range(size))
        res = dec.decode(enc.encode(x))
        assert res == x

    @pytest.mark.parametrize("typ", [set, Set, Set[Any]])
    def test_set_any(self, typ):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(typ)
        x = {1, "two", b"three"}
        res = dec.decode(enc.encode(x))
        assert res == x
        with pytest.raises(msgspec.DecodingError, match="expected `set`"):
            dec.decode(enc.encode(1))

    def test_set_typed(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Set[int])
        x = {1, 2, 3}
        res = dec.decode(enc.encode(x))
        assert res == x
        with pytest.raises(msgspec.DecodingError, match="expected `int`"):
            dec.decode(enc.encode({1, 2, "three"}))

    @pytest.mark.parametrize("size", SIZES)
    def test_vartuple_lengths(self, size):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(tuple)
        x = tuple(range(size))
        res = dec.decode(enc.encode(x))
        assert res == x

    @pytest.mark.parametrize("typ", [tuple, Tuple, Tuple[Any, ...]])
    def test_vartuple_any(self, typ):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(typ)
        x = (1, "two", b"three")
        res = dec.decode(enc.encode(x))
        assert res == x
        with pytest.raises(msgspec.DecodingError, match="expected `tuple`"):
            dec.decode(enc.encode(1))

    def test_vartuple_typed(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Tuple[int, ...])
        x = (1, 2, 3)
        res = dec.decode(enc.encode(x))
        assert res == x
        with pytest.raises(msgspec.DecodingError, match="expected `int`"):
            dec.decode(enc.encode((1, 2, "three")))

    def test_fixtuple_any(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Tuple[Any, Any, Any])
        x = (1, "two", b"three")
        res = dec.decode(enc.encode(x))
        assert res == x
        with pytest.raises(msgspec.DecodingError, match="expected `tuple`"):
            dec.decode(enc.encode(1))
        with pytest.raises(
                msgspec.DecodingError,
                match=
                r"Error decoding `Tuple\[Any, Any, Any\]`: expected tuple of length 3, got 2",
        ):
            dec.decode(enc.encode((1, 2)))

    def test_fixtuple_typed(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Tuple[int, str, bytes])
        x = (1, "two", b"three")
        res = dec.decode(enc.encode(x))
        assert res == x
        with pytest.raises(msgspec.DecodingError, match="expected `bytes`"):
            dec.decode(enc.encode((1, "two", "three")))
        with pytest.raises(
                msgspec.DecodingError,
                match=
                r"Error decoding `Tuple\[int, str, bytes\]`: expected tuple of length 3, got 2",
        ):
            dec.decode(enc.encode((1, 2)))

    @pytest.mark.parametrize("size", SIZES)
    def test_dict_lengths(self, size):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(dict)
        x = {i: i for i in range(size)}
        res = dec.decode(enc.encode(x))
        assert res == x

    @pytest.mark.parametrize("typ", [dict, Dict, Dict[Any, Any]])
    def test_dict_any_any(self, typ):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(typ)
        x = {1: "one", "two": 2, b"three": 3.0}
        res = dec.decode(enc.encode(x))
        assert res == x
        with pytest.raises(msgspec.DecodingError, match="expected `dict`"):
            dec.decode(enc.encode(1))

    def test_dict_any_val(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Dict[str, Any])
        x = {"a": 1, "b": "two", "c": b"three"}
        res = dec.decode(enc.encode(x))
        assert res == x
        with pytest.raises(msgspec.DecodingError, match="expected `str`"):
            dec.decode(enc.encode({1: 2}))

    def test_dict_any_key(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Dict[Any, str])
        x = {1: "a", "two": "b", b"three": "c"}
        res = dec.decode(enc.encode(x))
        assert res == x
        with pytest.raises(msgspec.DecodingError, match="expected `str`"):
            dec.decode(enc.encode({1: 2}))

    def test_dict_typed(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Dict[str, int])
        x = {"a": 1, "b": 2}
        res = dec.decode(enc.encode(x))
        assert res == x
        with pytest.raises(msgspec.DecodingError, match="expected `str`"):
            dec.decode(enc.encode({1: 2}))
        with pytest.raises(msgspec.DecodingError, match="expected `int`"):
            dec.decode(enc.encode({"a": "two"}))

    def test_enum(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(FruitStr)

        a = enc.encode(FruitStr.APPLE)
        assert enc.encode("APPLE") == a
        assert dec.decode(a) == FruitStr.APPLE

        with pytest.raises(msgspec.DecodingError, match="truncated"):
            dec.decode(a[:-2])
        with pytest.raises(msgspec.DecodingError,
                           match="Error decoding enum `FruitStr`"):
            dec.decode(enc.encode("MISSING"))
        with pytest.raises(msgspec.DecodingError):
            dec.decode(enc.encode(1))

    def test_int_enum(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(FruitInt)

        a = enc.encode(FruitInt.APPLE)
        assert enc.encode(1) == a
        assert dec.decode(a) == FruitInt.APPLE

        with pytest.raises(msgspec.DecodingError, match="truncated"):
            dec.decode(a[:-2])
        with pytest.raises(msgspec.DecodingError,
                           match="Error decoding enum `FruitInt`"):
            dec.decode(enc.encode(1000))
        with pytest.raises(msgspec.DecodingError):
            dec.decode(enc.encode("INVALID"))

    def test_struct(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Person)

        x = Person(first="harry", last="potter", age=13)
        a = enc.encode(x)
        assert (enc.encode({
            "first": "harry",
            "last": "potter",
            "age": 13,
            "prefect": False
        }) == a)
        assert dec.decode(a) == x

        with pytest.raises(msgspec.DecodingError, match="truncated"):
            dec.decode(a[:-2])

        with pytest.raises(msgspec.DecodingError, match="expected `struct`"):
            dec.decode(enc.encode(1))

        with pytest.raises(
                msgspec.DecodingError,
                match=
                r"Error decoding `Person` field `first` \(`str`\): expected `str`, got `int`",
        ):
            dec.decode(enc.encode({1: "harry"}))

    def test_struct_field_wrong_type(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Person)

        bad = enc.encode({
            "first": "harry",
            "last": "potter",
            "age": "thirteen"
        })
        with pytest.raises(msgspec.DecodingError, match="expected `int`"):
            dec.decode(bad)

    def test_struct_missing_fields(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Person)

        bad = enc.encode({"first": "harry", "last": "potter"})
        with pytest.raises(msgspec.DecodingError,
                           match="missing required field `age`"):
            dec.decode(bad)

        bad = enc.encode({})
        with pytest.raises(msgspec.DecodingError,
                           match="missing required field `first`"):
            dec.decode(bad)

    @pytest.mark.parametrize(
        "extra",
        [
            None,
            False,
            True,
            1,
            2.0,
            "three",
            b"four",
            [1, 2],
            {
                3: 4
            },
            msgspec.Ext(1, b"12345"),
            msgspec.Ext(1, b""),
        ],
    )
    def test_struct_ignore_extra_fields(self, extra):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Person)

        a = enc.encode({
            "extra1": extra,
            "first": "harry",
            "extra2": extra,
            "last": "potter",
            "age": 13,
            "extra3": extra,
        })
        res = dec.decode(a)
        assert res == Person("harry", "potter", 13)

    def test_struct_defaults_missing_fields(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Person)

        a = enc.encode({"first": "harry", "last": "potter", "age": 13})
        res = dec.decode(a)
        assert res == Person("harry", "potter", 13)
        assert res.prefect is False

    def test_struct_gc_maybe_untracked_on_decode(self):
        class Test(msgspec.Struct):
            x: Any
            y: Any
            z: Tuple = ()

        enc = msgspec.Encoder()
        dec = msgspec.Decoder(List[Test])

        ts = [
            Test(1, 2),
            Test(3, "hello"),
            Test([], []),
            Test({}, {}),
            Test(None, None, ()),
        ]
        a, b, c, d, e = dec.decode(enc.encode(ts))
        assert not gc.is_tracked(a)
        assert not gc.is_tracked(b)
        assert gc.is_tracked(c)
        assert gc.is_tracked(d)
        assert not gc.is_tracked(e)

    def test_struct_recursive_definition(self):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Node)

        x = Node(Node(Node(), Node(Node())))
        s = enc.encode(x)
        res = dec.decode(s)
        assert res == x

    @pytest.mark.parametrize(
        "typ, value",
        [
            (bool, False),
            (bool, True),
            (int, 1),
            (float, 2.5),
            (str, "a"),
            (bytes, b"a"),
            (bytearray, bytearray(b"a")),
            (FruitInt, FruitInt.APPLE),
            (FruitStr, FruitStr.APPLE),
            (Person, Person("harry", "potter", 13)),
            (list, [1]),
            (set, {1}),
            (tuple, (1, 2)),
            (Tuple[int, int], (1, 2)),
            (dict, {
                1: 2
            }),
        ],
    )
    def test_optional(self, typ, value):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(Optional[typ])

        s = enc.encode(value)
        s2 = enc.encode(None)
        assert dec.decode(s) == value
        assert dec.decode(s2) is None

        dec = msgspec.Decoder(typ)
        with pytest.raises(msgspec.DecodingError):
            dec.decode(s2)

    @pytest.mark.parametrize(
        "typ, value",
        [
            (List[Optional[int]], [1, None]),
            (Tuple[Optional[int], int], (None, 1)),
            (Set[Optional[int]], {1, None}),
            (Dict[str, Optional[int]], {
                "a": 1,
                "b": None
            }),
            (Dict[Optional[str], int], {
                "a": 1,
                None: 2
            }),
        ],
    )
    def test_optional_nested(self, typ, value):
        enc = msgspec.Encoder()
        dec = msgspec.Decoder(typ)

        s = enc.encode(value)
        assert dec.decode(s) == value

    def test_decoding_error_no_struct_toplevel(self):
        b = msgspec.Encoder().encode([{"a": 1}])
        dec = msgspec.Decoder(List[Dict[str, str]])
        with pytest.raises(
                msgspec.DecodingError,
                match=
                r"Error decoding `List\[Dict\[str, str\]\]`: expected `str`, got `int`",
        ):
            dec.decode(b)
Пример #10
0
 def test_immutable(self):
     x = msgspec.Ext(1, b"two")
     with pytest.raises(AttributeError):
         x.code = 2
Пример #11
0
 def test_code_wrong_type(self):
     with pytest.raises(TypeError):
         msgspec.Ext(b"bad", b"bad")
Пример #12
0
 def test_data_wrong_type(self):
     with pytest.raises(TypeError):
         msgspec.Ext(1, 2)
Пример #13
0
 def test_code_out_of_range(self, code):
     with pytest.raises(ValueError):
         msgspec.Ext(code, b"bad")
Пример #14
0
 def test_init(self, data):
     x = msgspec.Ext(1, data)
     assert x.code == 1
     assert x.data == data
Пример #15
0
 def test_decoder_ext_hook_bad_signature(self):
     msg = msgspec.encode(range(5),
                          default=lambda x: msgspec.Ext(1, b"test"))
     with pytest.raises(TypeError):
         msgspec.decode(msg, ext_hook=lambda: None)
Пример #16
0
 def test_pickleable(self):
     x = msgspec.Ext(1, b"two")
     x2 = pickle.loads(pickle.dumps(x))
     assert x2.code == 1
     assert x2.data == b"two"
Пример #17
0
def check_Ext() -> None:
    ext = msgspec.Ext(1, b"test")
    reveal_type(ext.code)  # assert "int" in typ
    reveal_type(ext.data)  # assert "bytes" in typ