File: C:/Users/fred/anaconda3/Lib/site-packages/astropy/cosmology/parameter/tests/test_descriptors.py
# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""Testing :mod:`astropy.cosmology.parameter._descriptor`."""
from __future__ import annotations
import re
import sys
from types import MappingProxyType
from typing import TYPE_CHECKING, ClassVar
import pytest
from astropy.cosmology._utils import all_cls_vars
from astropy.cosmology.parameter import Parameter
from astropy.cosmology.parameter._descriptors import ParametersAttribute
if TYPE_CHECKING:
from astropy.cosmology.core import Cosmology
class Obj:
"""Example class with a ParametersAttribute."""
# Attributes that will be accessed by ParametersAttribute when called an instance
# of this class. On a Cosmology these would be the Parameter objects.
a: ClassVar[int] = 1
b: ClassVar[int] = 2
c: ClassVar[int] = 3
# The class attribute that is accessed by ParametersAttribute when called on the
# class. On a Cosmology this would be the mapping of Parameter objects.
# Here it is just the names of the attributes that will be accessed by the
# ParametersAttribute to better distinguish between the class and instance
# attributes.
_attr_map: ClassVar[tuple[str, ...]] = ("a", "b", "c")
# The ParametersAttribute descriptor. This will return a mapping of the values of
# the attributes listed in ``_attr_map`` when called on an instance of this class.
# When called on the class, it will return ``_attr_map`` itself.
attr = ParametersAttribute(attr_name="_attr_map")
class TestParametersAttribute:
"""Test the descriptor ``ParametersAttribute``."""
def test_init(self) -> None:
"""Test constructing a ParametersAttribute."""
# Proper construction
attr = ParametersAttribute("attr_name")
assert attr.attr_name == "attr_name"
# Improper construction
# There isn't type checking on the attr_name, so this is allowed, but will fail
# later when the descriptor is used.
attr = ParametersAttribute(1) # type: ignore[arg-type]
assert attr.attr_name == 1
def test_get_from_class(self) -> None:
"""Test the descriptor ``__get__`` from the class."""
assert Obj.attr == ("a", "b", "c")
def test_get_from_instance(self) -> None:
"""Test the descriptor ``__get__``."""
obj = Obj() # Construct an instance for the attribute `attr`.
assert isinstance(obj.attr, MappingProxyType)
assert tuple(obj.attr.keys()) == obj._attr_map
def test_set_from_instance(self) -> None:
"""Test the descriptor ``__set__``."""
obj = Obj() # Construct an instance for the attribute `attr`.
with pytest.raises(AttributeError, match="cannot set 'attr' of"):
obj.attr = {}
def test_descriptor_attr_name_not_str(self) -> None:
"""Test when ``attr_name`` is not a string and used as a descriptor.
This is a regression test for #15882.
"""
class Obj2(Obj):
attr = ParametersAttribute(attr_name=None) # type: ignore[arg-type]
obj = Obj2()
if sys.version_info < (3, 11):
msg = "getattr(): attribute name must be string"
else:
msg = "attribute name must be string, not 'NoneType'"
with pytest.raises(TypeError, match=re.escape(msg)):
_ = obj.attr
##############################################################################
class ParametersAttributeTestMixin:
"""Test the descriptor for ``parameters`` on Cosmology classes.
This is a mixin class and is mixed into
:class:`~astropy.cosmology.tests.test_core.CosmologyTest`.
"""
@pytest.mark.parametrize("name", ["parameters", "_derived_parameters"])
def test_parameters_from_class(self, cosmo_cls: type[Cosmology], name: str) -> None:
"""Test descriptor ``parameters`` accessed from the class."""
# test presence
assert hasattr(cosmo_cls, name)
# test Parameter is a MappingProxyType
parameters = getattr(cosmo_cls, name)
assert isinstance(parameters, MappingProxyType)
# Test items
assert all(isinstance(p, Parameter) for p in parameters.values())
assert set(parameters) == {
k
for k, v in all_cls_vars(cosmo_cls).items()
if (isinstance(v, Parameter) and (v.derived == ("derived" in name)))
}
@pytest.mark.parametrize("name", ["parameters", "_derived_parameters"])
def test_parameters_from_instance(self, cosmo: Cosmology, name: str) -> None:
"""Test descriptor ``parameters`` accessed from the instance."""
# test presence
assert hasattr(cosmo, name)
# test Parameter is a MappingProxyType
parameters = getattr(cosmo, name)
assert isinstance(parameters, MappingProxyType)
# Test keys
assert set(parameters) == {
k
for k, v in all_cls_vars(cosmo).items()
if (isinstance(v, Parameter) and (v.derived == ("derived" in name)))
}
@pytest.mark.parametrize("name", ["parameters", "_derived_parameters"])
def test_parameters_cannot_set_on_instance(
self, cosmo: Cosmology, name: str
) -> None:
"""Test descriptor ``parameters`` cannot be set on the instance."""
with pytest.raises(AttributeError, match=f"cannot assign to field {name!r}"):
setattr(cosmo, name, {})