Skip to content

Commit

Permalink
Added use_dict to Attr and Prop
Browse files Browse the repository at this point in the history
  • Loading branch information
sg495 committed Mar 6, 2024
1 parent 2d4179d commit a7058f2
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 13 deletions.
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[mypy]
strict = True
plugins = typed_descriptors.mypy_plugin, mypy.plugins.proper_plugin
plugins = .\typed_descriptors\mypy_plugin.py, mypy.plugins.proper_plugin
16 changes: 12 additions & 4 deletions typed_descriptors/attr.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ def validator(
*,
readonly: bool = False,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> Attr[T]: ...

@staticmethod
Expand All @@ -151,6 +152,7 @@ def validator(
*,
readonly: bool = False,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> ValidatedAttrFactory: ...

@staticmethod
Expand All @@ -160,6 +162,7 @@ def validator(
*,
readonly: bool = False,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> ValidatedAttrFactory | Attr[T]:
"""
Decorator used to create an :class:`Attr` from a validator function,
Expand Down Expand Up @@ -208,11 +211,15 @@ def w(self, value: Sequence[int]) -> bool:
validator=validator_fun,
readonly=readonly,
backed_by=backed_by,
use_dict=use_dict,
)

def _validated_attr(validator_fun: ValidatorFunction[_T]) -> Attr[_T]:
return Attr.validator(
validator_fun, readonly=readonly, backed_by=backed_by
validator_fun,
readonly=readonly,
backed_by=backed_by,
use_dict=use_dict,
)

return _validated_attr
Expand All @@ -229,6 +236,7 @@ def __init__(
*,
readonly: bool = False,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> None:
# pylint: disable = redefined-builtin
...
Expand All @@ -242,6 +250,7 @@ def __init__(
*,
readonly: bool = False,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> None:
# pylint: disable = redefined-builtin
...
Expand All @@ -254,23 +263,22 @@ def __init__(
*,
readonly: bool = False,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> None:
"""
Creates a new attribute with the given type and optional validator.
:param ty: the type of the attribute
:param validator: an optional validator function for the attribute
:param readonly: whether the attribute is read-only
:param backed_by: the name of the backing attribute for the
attribute, or :obj:`None` to use a default name
:raises TypeError: if the type is not a valid type
:raises TypeError: if the validator is not callable
:meta public:
"""
# pylint: disable = redefined-builtin
super().__init__(type, backed_by=backed_by)
super().__init__(type, backed_by=backed_by, use_dict=use_dict)
if validator is not None:
validate_validator_fun(validator)
self.__doc__ = validator.__doc__
Expand Down
3 changes: 3 additions & 0 deletions typed_descriptors/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class instances, resulting in incorrect behaviour.
return True
return False


def class_slots(cls: type) -> tuple[str, ...] | None:
"""
Returns a tuple consisting of all slots for the given class and all
Expand All @@ -89,6 +90,7 @@ def class_slots(cls: type) -> tuple[str, ...] | None:
slots.append(slot)
return tuple(slots)


def name_mangle(owner: type, attr_name: str) -> str:
"""
If the given attribute name is private and not dunder,
Expand Down Expand Up @@ -157,6 +159,7 @@ def __get__(self, instance: Any, _: Type[Any]) -> T_co | Self:
:meta public:
"""


class DescriptorBase(TypedDescriptor[T]):
"""
Base class for descriptors backed by an attribute whose name and access mode
Expand Down
4 changes: 4 additions & 0 deletions typed_descriptors/mypy_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from mypy.plugin import FunctionContext, Plugin
from mypy.types import CallableType, Instance, get_proper_type, Type


def typed_descriptor_hook(ctx: FunctionContext) -> Type:
"""
Extracts the descriptor type and assigns it to the generic type parameter,
Expand All @@ -26,11 +27,13 @@ def typed_descriptor_hook(ctx: FunctionContext) -> Type:
return ret_t
return ret_t


_function_hooks = {
"typed_descriptors.attr.Attr": typed_descriptor_hook,
"typed_descriptors.prop.Prop": typed_descriptor_hook,
}


class TypedDescriptorsPlugin(Plugin):
"""
Mypy plugin which expands type inference for typed descriptor,
Expand All @@ -45,6 +48,7 @@ def get_function_hook(
return hook
return super().get_function_hook(fullname)


def plugin(version: str) -> typing.Type[TypedDescriptorsPlugin]:
"""
Entry point for the Mypy plugin.
Expand Down
43 changes: 35 additions & 8 deletions typed_descriptors/prop.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,21 @@ class Prop(DescriptorBase[T]):
@staticmethod
@overload
def value(
value_fun: ValueFunction[T], /, *, backed_by: Optional[str] = None
value_fun: ValueFunction[T],
/,
*,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> Prop[T]: ...

@staticmethod
@overload
def value(
value_fun: None = None, /, *, backed_by: Optional[str] = None
value_fun: None = None,
/,
*,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> PropFactory: ...

@staticmethod
Expand All @@ -118,6 +126,7 @@ def value(
/,
*,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> PropFactory | Prop[T]:
"""
An alias for :func:`cached_property`.
Expand All @@ -126,7 +135,9 @@ def value(
aligned to the :meth:`Attr.validator<typed_descriptors.attr.Attr.validator>`
decorator for attributes.
"""
return cached_property(value_fun, backed_by=backed_by)
return cached_property(
value_fun, backed_by=backed_by, use_dict=use_dict
)

__value_fun: ValueFunction[T]

Expand All @@ -138,6 +149,7 @@ def __init__(
/,
*,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> None:
# pylint: disable = redefined-builtin
...
Expand All @@ -150,6 +162,7 @@ def __init__(
/,
*,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> None:
# pylint: disable = redefined-builtin
...
Expand All @@ -161,6 +174,7 @@ def __init__(
/,
*,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> None:
"""
Creates a new property with the given type and value function.
Expand All @@ -177,7 +191,7 @@ def __init__(
"""
# pylint: disable = redefined-builtin
validate_value_fun(value)
super().__init__(type, backed_by=backed_by)
super().__init__(type, backed_by=backed_by, use_dict=use_dict)
if not callable(value):
raise TypeError(f"Expected callable 'value', got {value!r}.")
self.__value_fun = value
Expand Down Expand Up @@ -310,13 +324,21 @@ def __repr__(self) -> str:

@overload
def cached_property(
value_fun: ValueFunction[T], /, *, backed_by: Optional[str] = None
value_fun: ValueFunction[T],
/,
*,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> Prop[T]: ...


@overload
def cached_property(
value_fun: None = None, /, *, backed_by: Optional[str] = None
value_fun: None = None,
/,
*,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> PropFactory: ...


Expand All @@ -325,6 +347,7 @@ def cached_property(
/,
*,
backed_by: Optional[str] = None,
use_dict: Optional[bool] = None,
) -> PropFactory | Prop[T]:
"""
Decorator used to create a cached property from a value function,
Expand Down Expand Up @@ -368,9 +391,13 @@ def x(self) -> Sequence[str]:
"""
if value_fun is not None:
prop_type = value_fun_return_type(value_fun)
return Prop(prop_type, value_fun, backed_by=backed_by)
return Prop(
prop_type, value_fun, backed_by=backed_by, use_dict=use_dict
)

def _cached_property(value_fun: ValueFunction[_T]) -> Prop[_T]:
return cached_property(value_fun, backed_by=backed_by)
return cached_property(
value_fun, backed_by=backed_by, use_dict=use_dict
)

return _cached_property

0 comments on commit a7058f2

Please sign in to comment.