|
56 | 56 | T = TypeVar("T") |
57 | 57 |
|
58 | 58 |
|
| 59 | +def make_new_alias(alias): |
| 60 | + """Replicate the logic to produce a types._GenericAlias from a typing.GenericAlias""" |
| 61 | + return typing._GenericAlias( |
| 62 | + alias.__origin__, alias.__origin__.__parameters__ |
| 63 | + ).__getitem__(*alias.__args__) |
| 64 | + |
| 65 | + |
59 | 66 | def assert_eq(x, y): |
60 | 67 | assert x == y |
61 | 68 | assert type(x) is type(y) |
@@ -1634,6 +1641,47 @@ def __iter__(self): |
1634 | 1641 | msgspec.msgpack.encode(mod.Foo({"x": 1})), type=mod.Foo[int] |
1635 | 1642 | ) |
1636 | 1643 |
|
| 1644 | + @pytest.mark.parametrize( |
| 1645 | + "mapping_type", ["collections.abc.Mapping", "typing.Mapping"] |
| 1646 | + ) |
| 1647 | + @py312_plus |
| 1648 | + def test_inherited_builtin_generic_typevar_syntax_info_cached(self, mapping_type: str): |
| 1649 | + source = f""" |
| 1650 | + from msgspec import Struct, StructMeta |
| 1651 | + import collections |
| 1652 | + import abc |
| 1653 | + import typing |
| 1654 | +
|
| 1655 | + class CombinedMeta(StructMeta, abc.ABCMeta): |
| 1656 | + pass |
| 1657 | +
|
| 1658 | + class Foo[T]({mapping_type}[str, T], Struct, metaclass=CombinedMeta): |
| 1659 | + data: dict[str, T] |
| 1660 | +
|
| 1661 | + def __getitem__(self, x): |
| 1662 | + return self.data[x] |
| 1663 | +
|
| 1664 | + def __len__(self): |
| 1665 | + return len(self.data) |
| 1666 | +
|
| 1667 | + def __iter__(self): |
| 1668 | + return iter(self.data) |
| 1669 | + """ |
| 1670 | + |
| 1671 | + with temp_module(source) as mod: |
| 1672 | + typ = mod.Foo[int] |
| 1673 | + dec = msgspec.json.Decoder(typ) |
| 1674 | + info = make_new_alias(typ).__msgspec_cache__ |
| 1675 | + assert info is not None |
| 1676 | + assert sys.getrefcount(info) <= 4 # info + attr + decoder + func call |
| 1677 | + dec2 = msgspec.json.Decoder(typ) |
| 1678 | + assert make_new_alias(typ).__msgspec_cache__ is info |
| 1679 | + assert sys.getrefcount(info) <= 5 |
| 1680 | + |
| 1681 | + del dec |
| 1682 | + del dec2 |
| 1683 | + assert sys.getrefcount(info) <= 3 |
| 1684 | + |
1637 | 1685 |
|
1638 | 1686 | class TestStructPostInit: |
1639 | 1687 | @pytest.mark.parametrize("array_like", [False, True]) |
@@ -1953,6 +2001,46 @@ def __iter__(self): |
1953 | 2001 | msgspec.msgpack.encode(mod.Foo({"x": 1})), type=mod.Foo[int] |
1954 | 2002 | ) |
1955 | 2003 |
|
| 2004 | + @pytest.mark.parametrize( |
| 2005 | + "mapping_type", ["collections.abc.Mapping", "typing.Mapping"] |
| 2006 | + ) |
| 2007 | + @py312_plus |
| 2008 | + def test_inherited_builtin_generic_typevar_syntax_info_cached( |
| 2009 | + self, mapping_type: str |
| 2010 | + ): |
| 2011 | + source = f""" |
| 2012 | + import dataclasses |
| 2013 | + import collections |
| 2014 | + import typing |
| 2015 | +
|
| 2016 | + @dataclasses.dataclass |
| 2017 | + class Foo[T]({mapping_type}[str, T]): |
| 2018 | + data: dict[str, T] |
| 2019 | +
|
| 2020 | + def __getitem__(self, x): |
| 2021 | + return self.data[x] |
| 2022 | +
|
| 2023 | + def __len__(self): |
| 2024 | + return len(self.data) |
| 2025 | +
|
| 2026 | + def __iter__(self): |
| 2027 | + return iter(self.data) |
| 2028 | + """ |
| 2029 | + |
| 2030 | + with temp_module(source) as mod: |
| 2031 | + typ = mod.Foo[int] |
| 2032 | + dec = msgspec.json.Decoder(typ) |
| 2033 | + info = make_new_alias(typ).__msgspec_cache__ |
| 2034 | + assert info is not None |
| 2035 | + assert sys.getrefcount(info) <= 4 # info + attr + decoder + func call |
| 2036 | + dec2 = msgspec.json.Decoder(typ) |
| 2037 | + assert make_new_alias(typ).__msgspec_cache__ is info |
| 2038 | + assert sys.getrefcount(info) <= 5 |
| 2039 | + |
| 2040 | + del dec |
| 2041 | + del dec2 |
| 2042 | + assert sys.getrefcount(info) <= 3 |
| 2043 | + |
1956 | 2044 |
|
1957 | 2045 | class TestStructOmitDefaults: |
1958 | 2046 | def test_omit_defaults(self, proto): |
|
0 commit comments