Source code for appstoreserverlibrary.models.LibraryUtility
# Copyright (c) 2023 Apple Inc. Licensed under MIT License.
from enum import EnumMeta
from functools import lru_cache
from typing import Any, List, Type, TypeVar
from uuid import UUID
from attr import Attribute, has, ib, fields
from cattr import override
from cattrs.gen import make_dict_structure_fn, make_dict_unstructure_fn, override
import cattrs
T = TypeVar('T')
metadata_key = 'correspondingFieldName'
metadata_type_key = 'typeOfField'
[docs]
class AppStoreServerLibraryEnumMeta(EnumMeta):
def __contains__(c, val):
try:
c(val)
except ValueError:
return False
return True
[docs]
def create_main_attr(c, raw_field_name: str, raw_required: bool = False) -> Any:
def value_set(self, _: Attribute, value: c):
newValue = value.value if value is not None else None
if raw_required and newValue is None:
raise ValueError(f"{raw_field_name} cannot be set to None when field is required")
if newValue != getattr(self, raw_field_name):
object.__setattr__(self, raw_field_name, newValue)
return value
return ib(default=None, on_setattr=value_set, metadata={metadata_key: raw_field_name, metadata_type_key: 'main'})
[docs]
def create_raw_attr(c, field_name: str, required: bool = False) -> Any:
def value_set(self, _: Attribute, value: str):
if required and value is None:
raise ValueError(f"raw{field_name[0].upper() + field_name[1:]} cannot be None")
newValue = c(value) if value in c else None
if newValue != getattr(self, field_name):
object.__setattr__(self, field_name, newValue)
return value
def validate_not_none(instance, attribute, value):
if value is None:
raise ValueError(f"{attribute.name} cannot be None")
if required:
from attr import Factory
def factory(instance):
main_value = getattr(instance, field_name)
if main_value is not None:
return main_value.value
raise ValueError(f"Either {field_name} or raw{field_name[0].upper() + field_name[1:]} must be provided")
return ib(default=Factory(factory, takes_self=True), kw_only=True, on_setattr=value_set, validator=validate_not_none, metadata={metadata_key: field_name, metadata_type_key: 'raw'})
else:
return ib(default=None, kw_only=True, on_setattr=value_set, metadata={metadata_key: field_name, metadata_type_key: 'raw'})
[docs]
class AttrsRawValueAware:
def __attrs_post_init__(self):
attr_fields: List[Attribute] = fields(type(self))
for attribute in attr_fields:
if metadata_type_key not in attribute.metadata or attribute.metadata[metadata_type_key] != 'raw':
continue
field: str = attribute.metadata.get(metadata_key)
rawField = 'raw' + field[0].upper() + field[1:]
rawValue = getattr(self, rawField)
value = getattr(self, field)
if rawValue is not None:
setattr(self, rawField, rawValue)
elif value is not None:
setattr(self, field, value)
@lru_cache(maxsize=None)
def _get_cattrs_converter(destination_class: Type[T]) -> cattrs.Converter:
c = cattrs.Converter()
# Register UUID hooks to ensure lowercase serialization
c.register_unstructure_hook(UUID, lambda uuid: str(uuid).lower() if uuid is not None else None)
c.register_structure_hook(UUID, lambda d, _: UUID(d) if d is not None else None)
# Need a function here because it must be a lambda based on cl, which is not always destination_class
def make_overrides(cl):
attributes: List[Attribute] = fields(cl)
cattrs_overrides = {}
# Use omit_if_default to prevent null fields from being serialized to JSON
for attribute in attributes:
if metadata_type_key in attribute.metadata:
matching_name: str = attribute.metadata[metadata_key]
if attribute.metadata[metadata_type_key] == 'raw':
cattrs_overrides[matching_name] = override(omit=True)
raw_field = 'raw' + matching_name[0].upper() + matching_name[1:]
cattrs_overrides[raw_field] = override(rename=matching_name, omit_if_default=True)
elif attribute.default is None and attribute.name not in cattrs_overrides:
cattrs_overrides[attribute.name] = override(omit_if_default=True)
return cattrs_overrides
c.register_structure_hook_factory(has, lambda cl: make_dict_structure_fn(cl, c, **make_overrides(cl)))
c.register_unstructure_hook_factory(has, lambda cl: make_dict_unstructure_fn(cl, c, **make_overrides(cl)))
return c