| # SPDX-License-Identifier: MIT | |
| import types | |
| from typing import Protocol | |
| import pytest | |
| import attr | |
| from attr._compat import _IS_GENERATOR_RESULTS, _lazy_is_generator | |
| @pytest.fixture(name="mp") | |
| def _mp(): | |
| return types.MappingProxyType({"x": 42, "y": "foo"}) | |
| class TestMetadataProxy: | |
| """ | |
| Ensure properties of metadata proxy independently of hypothesis strategies. | |
| """ | |
| def test_repr(self, mp): | |
| """ | |
| repr makes sense and is consistent across Python versions. | |
| """ | |
| assert any( | |
| [ | |
| "mappingproxy({'x': 42, 'y': 'foo'})" == repr(mp), | |
| "mappingproxy({'y': 'foo', 'x': 42})" == repr(mp), | |
| ] | |
| ) | |
| def test_immutable(self, mp): | |
| """ | |
| All mutating methods raise errors. | |
| """ | |
| with pytest.raises(TypeError, match="not support item assignment"): | |
| mp["z"] = 23 | |
| with pytest.raises(TypeError, match="not support item deletion"): | |
| del mp["x"] | |
| with pytest.raises(AttributeError, match="no attribute 'update'"): | |
| mp.update({}) | |
| with pytest.raises(AttributeError, match="no attribute 'clear'"): | |
| mp.clear() | |
| with pytest.raises(AttributeError, match="no attribute 'pop'"): | |
| mp.pop("x") | |
| with pytest.raises(AttributeError, match="no attribute 'popitem'"): | |
| mp.popitem() | |
| with pytest.raises(AttributeError, match="no attribute 'setdefault'"): | |
| mp.setdefault("x") | |
| def test_attrsinstance_subclass_protocol(): | |
| """ | |
| It's possible to subclass AttrsInstance and Protocol at once. | |
| """ | |
| class Foo(attr.AttrsInstance, Protocol): | |
| def attribute(self) -> int: ... | |
| class TestLazyIsGenerator: | |
| def test_is_generator(self): | |
| """ | |
| Returns True for generator functions and caches the result. | |
| """ | |
| def gen(): | |
| yield 1 | |
| assert _lazy_is_generator(gen)() | |
| assert _IS_GENERATOR_RESULTS[gen] is True | |
| def test_is_not_generator(self): | |
| """ | |
| Returns False for non-generator functions and caches the result. | |
| """ | |
| def non_gen(): | |
| return 1 | |
| assert not _lazy_is_generator(non_gen)() | |
| assert _IS_GENERATOR_RESULTS[non_gen] is False | |
| def test_not_hashable(self): | |
| """ | |
| If the user somehow manages to create a non-hashable function, | |
| the result is not hashed but the result is correct. | |
| """ | |
| non_hashable = {} | |
| assert not _lazy_is_generator(non_hashable)() |