typing: Annotate openstack.fields (2/2)

Change-Id: Ic25a63a048040d4a8bd39ceeedc7286ad0d50e7b
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane
2024-10-29 10:25:21 +00:00
parent 5ef01accc5
commit 9629d81a90
5 changed files with 112 additions and 109 deletions

View File

@@ -21,7 +21,6 @@ from openstack import warnings as os_warnings
_SEEN_FORMAT = '{name}_seen'
_T1 = ty.TypeVar('_T1')
_T2 = ty.TypeVar('_T2')
_T3 = ty.TypeVar('_T3', str, bool, int, float)
@@ -145,32 +144,33 @@ def _convert_type(
class _BaseComponent(abc.ABC):
# The name this component is being tracked as in the Resource
key: str
key: ty.ClassVar[str]
# The class to be used for mappings
_map_cls: type[ty.Mapping] = dict
_map_cls: ty.ClassVar[type[ty.MutableMapping[str, ty.Any]]] = dict
#: Marks the property as deprecated.
deprecated = False
#: Deprecation reason message used to warn users when deprecated == True
deprecation_reason = None
#: Control field used to manage the deprecation warning. We want to warn
#: only once when the attribute is retrieved in the code.
already_warned_deprecation = False
name: str
data_type: ty.Optional[ty.Any]
default: ty.Any
alias: ty.Optional[str]
aka: ty.Optional[str]
alternate_id: bool
list_type: ty.Optional[ty.Any]
coerce_to_default: bool
deprecated: bool
deprecation_reason: ty.Optional[str]
def __init__(
self,
name,
type=None,
default=None,
alias=None,
aka=None,
alternate_id=False,
list_type=None,
coerce_to_default=False,
deprecated=False,
deprecation_reason=None,
**kwargs,
name: str,
type: ty.Optional[ty.Any] = None,
default: ty.Any = None,
alias: ty.Optional[str] = None,
aka: ty.Optional[str] = None,
alternate_id: bool = False,
list_type: ty.Optional[ty.Any] = None,
coerce_to_default: bool = False,
deprecated: bool = False,
deprecation_reason: ty.Optional[str] = None,
):
"""A typed descriptor for a component that makes up a Resource
@@ -183,26 +183,21 @@ class _BaseComponent(abc.ABC):
:param default: Typically None, but any other default can be set.
:param alias: If set, alternative attribute on object to return.
:param aka: If set, additional name attribute would be available under.
:param alternate_id:
When `True`, this property is known internally as a value that
can be sent with requests that require an ID but when `id` is
not a name the Resource has. This is a relatively uncommon case,
and this setting should only be used once per Resource.
:param list_type:
If type is `list`, list_type designates what the type of the
elements of the list should be.
:param coerce_to_default:
If the Component is None or not present, force the given default
to be used. If a default is not given but a type is given,
construct an empty version of the type in question.
:param deprecated:
Indicates if the option is deprecated. If it is, we display a
warning message to the user.
:param deprecation_reason:
Custom deprecation message.
:param alternate_id: When `True`, this property is known internally as
a value that can be sent with requests that require an ID but when
`id` is not a name the Resource has. This is a relatively uncommon
case, and this setting should only be used once per Resource.
:param list_type: If type is `list`, list_type designates what the type
of the elements of the list should be.
:param coerce_to_default: If the Component is None or not present,
force the given default to be used. If a default is not given but a
type is given, construct an empty version of the type in question.
:param deprecated: Indicates if the option is deprecated. If it is, we
display a warning message to the user.
:param deprecation_reason: Custom deprecation message.
"""
self.name = name
self.type = type
self.data_type = type
if type is not None and coerce_to_default and not default:
self.default = type()
else:
@@ -216,7 +211,11 @@ class _BaseComponent(abc.ABC):
self.deprecated = deprecated
self.deprecation_reason = deprecation_reason
def __get__(self, instance, owner):
def __get__(
self,
instance: object,
owner: ty.Optional[type[object]] = None,
) -> ty.Any:
if instance is None:
return self
@@ -248,7 +247,7 @@ class _BaseComponent(abc.ABC):
self.warn_if_deprecated_property(value)
return value
# self.type() should not be called on None objects.
# self.data_type() should not be called on None objects.
if value is None:
return None
@@ -259,9 +258,14 @@ class _BaseComponent(abc.ABC):
if self.name != "tenant_id":
self.warn_if_deprecated_property(value)
return _convert_type(value, self.type, self.list_type)
return _convert_type(value, self.data_type, self.list_type)
def warn_if_deprecated_property(self, value):
@property
def type(self) -> ty.Optional[ty.Any]:
# deprecated alias proxy
return self.data_type
def warn_if_deprecated_property(self, value: ty.Any) -> None:
deprecated = object.__getattribute__(self, 'deprecated')
deprecation_reason = object.__getattribute__(
self,
@@ -275,18 +279,19 @@ class _BaseComponent(abc.ABC):
),
os_warnings.RemovedFieldWarning,
)
return value
def __set__(self, instance, value):
def __set__(self, instance: object, value: ty.Any) -> None:
if self.coerce_to_default and value is None:
value = self.default
if value != self.default:
value = _convert_type(value, self.type, self.list_type)
value_ = self.default
elif value != self.default:
value_ = _convert_type(value, self.data_type, self.list_type)
else:
value_ = value
attributes = getattr(instance, self.key)
attributes[self.name] = value
attributes[self.name] = value_
def __delete__(self, instance):
def __delete__(self, instance: object) -> None:
try:
attributes = getattr(instance, self.key)
del attributes[self.name]

View File

@@ -52,9 +52,9 @@ class MetadefProperty(resource.Resource):
#: that a string value must match.
pattern = resource.Body('pattern')
#: Minimum allowed string length.
min_length = resource.Body('minLength', type=int, minimum=0, default=0)
min_length = resource.Body('minLength', type=int, default=0)
#: Maximum allowed string length.
max_length = resource.Body('maxLength', type=int, minimum=0)
max_length = resource.Body('maxLength', type=int)
# FIXME(stephenfin): This is causing conflicts due to the 'dict.items'
# method. Perhaps we need to rename it?
#: Schema for the items in an array.
@@ -64,9 +64,9 @@ class MetadefProperty(resource.Resource):
'uniqueItems', type=bool, default=False
)
#: Minimum length of an array.
min_items = resource.Body('minItems', type=int, minimum=0, default=0)
min_items = resource.Body('minItems', type=int, default=0)
#: Maximum length of an array.
max_items = resource.Body('maxItems', type=int, minimum=0)
max_items = resource.Body('maxItems', type=int)
#: Describes extra items, if you use tuple typing. If the value of
#: ``items`` is an array (tuple typing) and the instance is longer than
#: the list of schemas in ``items``, the additional items are described by

View File

@@ -54,19 +54,21 @@ from openstack import warnings as os_warnings
LOG = _log.setup_logging(__name__)
# TODO(stephenfin): We should deprecate the 'type' and 'list_type' arguments
# for all of the below in favour of annotations. To that end, we have stuck
# with Any rather than generating super complex types
def Body(
name,
type=None,
default=None,
alias=None,
aka=None,
alternate_id=False,
list_type=None,
coerce_to_default=False,
deprecated=False,
deprecation_reason=None,
**kwargs,
):
name: str,
type: ty.Optional[ty.Any] = None,
default: ty.Any = None,
alias: ty.Optional[str] = None,
aka: ty.Optional[str] = None,
alternate_id: bool = False,
list_type: ty.Optional[ty.Any] = None,
coerce_to_default: bool = False,
deprecated: bool = False,
deprecation_reason: ty.Optional[str] = None,
) -> ty.Any:
return fields.Body(
name,
type=type,
@@ -78,23 +80,21 @@ def Body(
coerce_to_default=coerce_to_default,
deprecated=deprecated,
deprecation_reason=deprecation_reason,
**kwargs,
)
def Header(
name,
type=None,
default=None,
alias=None,
aka=None,
alternate_id=False,
list_type=None,
coerce_to_default=False,
deprecated=False,
deprecation_reason=None,
**kwargs,
):
name: str,
type: ty.Optional[ty.Any] = None,
default: ty.Any = None,
alias: ty.Optional[str] = None,
aka: ty.Optional[str] = None,
alternate_id: bool = False,
list_type: ty.Optional[ty.Any] = None,
coerce_to_default: bool = False,
deprecated: bool = False,
deprecation_reason: ty.Optional[str] = None,
) -> ty.Any:
return fields.Header(
name,
type=type,
@@ -106,23 +106,21 @@ def Header(
coerce_to_default=coerce_to_default,
deprecated=deprecated,
deprecation_reason=deprecation_reason,
**kwargs,
)
def URI(
name,
type=None,
default=None,
alias=None,
aka=None,
alternate_id=False,
list_type=None,
coerce_to_default=False,
deprecated=False,
deprecation_reason=None,
**kwargs,
):
name: str,
type: ty.Optional[ty.Any] = None,
default: ty.Any = None,
alias: ty.Optional[str] = None,
aka: ty.Optional[str] = None,
alternate_id: bool = False,
list_type: ty.Optional[ty.Any] = None,
coerce_to_default: bool = False,
deprecated: bool = False,
deprecation_reason: ty.Optional[str] = None,
) -> ty.Any:
return fields.URI(
name,
type=type,
@@ -134,23 +132,21 @@ def URI(
coerce_to_default=coerce_to_default,
deprecated=deprecated,
deprecation_reason=deprecation_reason,
**kwargs,
)
def Computed(
name,
type=None,
default=None,
alias=None,
aka=None,
alternate_id=False,
list_type=None,
coerce_to_default=False,
deprecated=False,
deprecation_reason=None,
**kwargs,
):
name: str,
type: ty.Optional[ty.Any] = None,
default: ty.Any = None,
alias: ty.Optional[str] = None,
aka: ty.Optional[str] = None,
alternate_id: bool = False,
list_type: ty.Optional[ty.Any] = None,
coerce_to_default: bool = False,
deprecated: bool = False,
deprecation_reason: ty.Optional[str] = None,
) -> ty.Any:
return fields.Computed(
name,
type=type,
@@ -162,7 +158,6 @@ def Computed(
coerce_to_default=coerce_to_default,
deprecated=deprecated,
deprecation_reason=deprecation_reason,
**kwargs,
)

View File

@@ -31,7 +31,7 @@ class ShareExportLocation(resource.Resource):
#: Properties
# The share ID, part of the URI for export locations
share_id = resource.URI("share_id", type='str')
share_id = resource.URI("share_id", type=str)
#: The path of the export location.
path = resource.Body("path", type=str)
#: Indicate if export location is preferred.

View File

@@ -29,7 +29,10 @@ exclude = '''
'''
[[tool.mypy.overrides]]
module = ["openstack.format"]
module = [
"openstack.fields",
"openstack.format",
]
warn_return_any = true
disallow_untyped_decorators = true
disallow_any_generics = true