text: #
  # Copyright (c), 2016-2020, SISSA (International School for Advanced Studies).
  # All rights reserved.
  # This file is distributed under the terms of the MIT License.
  # See the file 'LICENSE' in the root directory of the present
  # distribution, or http://opensource.org/licenses/MIT.
  #
  # @author Davide Brunato <brunato@sissa.it>
  #
  """
  This module contains classes for XML Schema attributes and attribute groups.
  """
  from decimal import Decimal
  from collections.abc import MutableMapping
  from elementpath.datatypes import AbstractDateTime, Duration, AbstractBinary
  from typing import Union, Dict, Optional
  
  from ..exceptions import XMLSchemaValueError
  from ..names import XSI_NAMESPACE, XSD_ANY_SIMPLE_TYPE, XSD_SIMPLE_TYPE, \
      XSD_ATTRIBUTE_GROUP, XSD_COMPLEX_TYPE, XSD_RESTRICTION, XSD_EXTENSION, \
      XSD_SEQUENCE, XSD_ALL, XSD_CHOICE, XSD_ATTRIBUTE, XSD_ANY_ATTRIBUTE, \
      XSD_ASSERT, XSD_NOTATION_TYPE, XSD_ANNOTATION
  from ..helpers import get_namespace, get_qname
  
  from .exceptions import XMLSchemaValidationError
  from .xsdbase import XsdComponent, ValidationMixin
  from .simple_types import XsdSimpleType
  from .wildcards import XsdAnyAttribute
  
  
  class XsdAttribute(XsdComponent, ValidationMixin):
      """
      Class for XSD 1.0 *attribute* declarations.
  
      :ivar type: the XSD simpleType of the attribute.
  
      ..  <attribute
            default = string
            fixed = string
            form = (qualified | unqualified)
            id = ID
            name = NCName
            ref = QName
            type = QName
            use = (optional | prohibited | required) : optional
            {any attributes with non-schema namespace ...}>
            Content: (annotation?, simpleType?)
          </attribute>
      """
      _ADMITTED_TAGS = {XSD_ATTRIBUTE}
  
      type = None
      qualified = False
      default = None
      fixed = None
      form = None
      use = 'optional'
      inheritable = False  # For XSD 1.1 attributes, always False for XSD 1.0 attributes.
  
      def _parse(self):
          attrib = self.elem.attrib
  
          if 'use' in attrib and self.parent is not None and \
                  attrib['use'] in {'optional', 'prohibited', 'required'}:
              self.use = attrib['use']
  
          if self._parse_reference():
              try:
                  xsd_attribute = self.maps.lookup_attribute(self.name)
              except LookupError:
                  self.type = self.any_simple_type
                  self.parse_error("unknown attribute {!r}".format(self.name))
              else:
                  self.ref = xsd_attribute
                  self.type = xsd_attribute.type
                  self.qualified = xsd_attribute.qualified
                  self.form = xsd_attribute.form
  
                  if xsd_attribute.default is not None and 'default' not in attrib:
                      self.default = xsd_attribute.default
  
                  if xsd_attribute.fixed is not None:
                      if 'fixed' not in attrib:
                          self.fixed = xsd_attribute.fixed
                      elif xsd_attribute.fixed != attrib['fixed']:
                          msg = "referenced attribute has a different fixed value {!r}"
                          self.parse_error(msg.format(xsd_attribute.fixed))
  
              for attribute in ('form', 'type'):
                  if attribute in self.elem.attrib:
                      self.parse_error("attribute {!r} is not allowed when "
                                       "attribute reference is used".format(attribute))
          else:
              if 'form' in attrib:
                  self.form = attrib['form']
                  if self.parent is not None and self.form == 'qualified':
                      self.qualified = True
              elif self.schema.attribute_form_default == 'qualified':
                  self.qualified = True
  
              try:
                  name = attrib['name']
              except KeyError:
                  pass
              else:
                  if name == 'xmlns':
                      self.parse_error("an attribute name must be different from 'xmlns'")
  
                  if self.parent is None or self.qualified:
                      if self.target_namespace == XSI_NAMESPACE and \
                              name not in {'nil', 'type', 'schemaLocation',
                                           'noNamespaceSchemaLocation'}:
                          self.parse_error("cannot add attributes in %r namespace" % XSI_NAMESPACE)
                      self.name = get_qname(self.target_namespace, name)
                  else:
                      self.name = name
  
              child = self._parse_child_component(self.elem)
              if 'type' in attrib:
                  try:
                      type_qname = self.schema.resolve_qname(attrib['type'])
                  except (KeyError, ValueError, RuntimeError) as err:
                      self.type = self.any_simple_type
                      self.parse_error(err)
                  else:
                      try:
                          self.type = self.maps.lookup_type(type_qname)
                      except LookupError as err:
                          self.type = self.any_simple_type
                          self.parse_error(err)
  
                      if child is not None and child.tag == XSD_SIMPLE_TYPE:
                          self.parse_error("ambiguous type definition for XSD attribute")
  
              elif child is not None:
                  # No 'type' attribute in declaration, parse for child local simpleType
                  self.type = self.schema.BUILDERS.simple_type_factory(child, self.schema, self)
              else:
                  # Empty declaration means xsdAnySimpleType
                  self.type = self.any_simple_type
  
              if not isinstance(self.type, XsdSimpleType):
                  self.type = self.any_simple_type
                  self.parse_error("XSD attribute's type must be a simpleType")
  
          # Check value constraints
          if 'default' in attrib:
              self.default = attrib['default']
              if 'fixed' in attrib:
                  self.parse_error("'default' and 'fixed' attributes are mutually exclusive")
  
              if self.use != 'optional':
                  self.parse_error("the attribute 'use' must be 'optional' "
                                   "if the attribute 'default' is present")
  
              if not self.type.is_valid(self.default):
                  msg = "default value {!r} is not compatible with attribute's type"
                  self.parse_error(msg.format(self.default))
              elif self.type.is_key() and self.xsd_version == '1.0':
                  self.parse_error("xs:ID key attributes cannot have a default value")
  
          elif 'fixed' in attrib:
              self.fixed = attrib['fixed']
              if not self.type.is_valid(self.fixed):
                  msg = "fixed value {!r} is not compatible with attribute's type"
                  self.parse_error(msg.format(self.fixed))
              elif self.type.is_key() and self.xsd_version == '1.0':
                  self.parse_error("xs:ID key attributes cannot have a fixed value")
  
      @property
      def built(self):
          return True
  
      @property
      def validation_attempted(self):
          return 'full'
  
      @property
      def scope(self):
          """The scope of the attribute declaration that can be 'global' or 'local'."""
          return 'global' if self.parent is None else 'local'
  
      @property
      def value_constraint(self):
          """The fixed or the default value if either is defined, `None` otherwise."""
          return self.fixed if self.fixed is not None else self.default
  
      def is_optional(self):
          return self.use == 'optional'
  
      def is_required(self):
          return self.use == 'required'
  
      def is_prohibited(self):
          return self.use == 'prohibited'
  
      def is_empty(self):
          return self.fixed == '' or self.type.is_empty()
  
      def iter_components(self, xsd_classes=None):
          if xsd_classes is None or isinstance(self, xsd_classes):
              yield self
          if self.ref is None and self.type.parent is not None:
              yield from self.type.iter_components(xsd_classes)
  
      def data_value(self, text):
          """Returns the decoded data value of the provided text as XPath fn:data()."""
          return self.decode(text, validation='skip')
  
      def iter_decode(self, text, validation='lax', **kwargs):
          if text is None and self.default is not None:
              text = self.default
  
          if self.type.is_notation():
              if self.type.name == XSD_NOTATION_TYPE:
                  msg = "cannot validate against xs:NOTATION directly, " \
                        "only against a subtype with an enumeration facet"
                  yield self.validation_error(validation, msg, text, **kwargs)
              elif not self.type.enumeration:
                  msg = "missing enumeration facet in xs:NOTATION subtype"
                  yield self.validation_error(validation, msg, text, **kwargs)
  
          if self.fixed is not None:
              if text is None:
                  text = self.fixed
              elif text != self.fixed and \
                      self.type.text_decode(text) != self.type.text_decode(self.fixed):
                  msg = "attribute {!r} has a fixed value {!r}".format(self.name, self.fixed)
                  yield self.validation_error(validation, msg, text, **kwargs)
  
          for value in self.type.iter_decode(text, validation, **kwargs):
              if isinstance(value, XMLSchemaValidationError):
                  value.reason = 'attribute {}={!r}: {}'.format(
                      self.prefixed_name, text, value.reason
                  )
                  yield value
                  continue
              elif 'value_hook' in kwargs:
                  yield kwargs['value_hook'](value, self.type)
              elif isinstance(value, (int, float, list)) or value is None:
                  yield value
              elif isinstance(value, str):
                  if value.startswith('{') and self.type.is_qname():
                      yield text
                  else:
                      yield value
              elif isinstance(value, Decimal):
                  try:
                      yield kwargs['decimal_type'](value)
                  except (KeyError, TypeError):
                      yield value
              elif isinstance(value, (AbstractDateTime, Duration)):
                  yield value if kwargs.get('datetime_types') else text.strip()
              elif isinstance(value, AbstractBinary) and not kwargs.get('binary_types'):
                  yield str(value)
              else:
                  yield value
              break  # pragma: no cover
  
      def iter_encode(self, obj, validation='lax', **kwargs):
          yield from self.type.iter_encode(obj, validation, **kwargs)
  
  
  class Xsd11Attribute(XsdAttribute):
      """
      Class for XSD 1.1 *attribute* declarations.
  
      ..  <attribute
            default = string
            fixed = string
            form = (qualified | unqualified)
            id = ID
            name = NCName
            ref = QName
            targetNamespace = anyURI
            type = QName
            use = (optional | prohibited | required) : optional
            inheritable = boolean
            {any attributes with non-schema namespace . . .}>
            Content: (annotation?, simpleType?)
          </attribute>
      """
      _target_namespace = None
  
      @property
      def target_namespace(self):
          if self._target_namespace is not None:
              return self._target_namespace
          elif self.ref is not None:
              return self.ref.target_namespace
          else:
              return self.schema.target_namespace
  
      def _parse(self):
          super()._parse()
          if self.use == 'prohibited' and 'fixed' in self.elem.attrib:
              self.parse_error("attribute 'fixed' with use=prohibited is not allowed in XSD 1.1")
          if 'inheritable' in self.elem.attrib:
              if self.elem.attrib['inheritable'].strip() in {'true', '1'}:
                  self.inheritable = True
          self._parse_target_namespace()
  
  
  class XsdAttributeGroup(MutableMapping, XsdComponent, ValidationMixin):
      """
      Class for XSD *attributeGroup* definitions.
  
      .. <attributeGroup
            id = ID
            name = NCName
            ref = QName
            {any attributes with non-schema namespace . . .}>
            Content: (annotation?, ((attribute | attributeGroup)*, anyAttribute?))
          </attributeGroup>
      """
      _ADMITTED_TAGS = {
          XSD_ATTRIBUTE_GROUP, XSD_COMPLEX_TYPE, XSD_RESTRICTION, XSD_EXTENSION,
          XSD_SEQUENCE, XSD_ALL, XSD_CHOICE, XSD_ATTRIBUTE, XSD_ANY_ATTRIBUTE
      }
  
      def __init__(self, elem, schema, parent, derivation=None, base_attributes=None):
          self.derivation = derivation
          self._attribute_group: Dict[str, Union[XsdAttribute, XsdAnyAttribute]] = {}
          self.base_attributes = base_attributes
          XsdComponent.__init__(self, elem, schema, parent)
  
      def __repr__(self):
          if self.name is not None:
              return '%s(name=%r)' % (self.__class__.__name__, self.name)
          elif self:
              names = [a if a.name is None else a.name for a in self.values()]
              return '%s(%r)' % (self.__class__.__name__, names)
          else:
              return '%s()' % self.__class__.__name__
  
      # Implementation of abstract methods
      def __getitem__(self, key):
          return self._attribute_group[key]
  
      def __setitem__(self, key, value: Union[XsdAttribute, XsdAnyAttribute]):
          if value.name != key:
              raise XMLSchemaValueError("%r name and key %r mismatch" % (value.name, key))
          self._attribute_group[key] = value
  
      def __delitem__(self, key):
          del self._attribute_group[key]
  
      def __iter__(self):
          if None in self._attribute_group:
              # Put AnyAttribute ('None' key) at the end of iteration
              return iter(sorted(self._attribute_group, key=lambda x: (x is None, x)))
          else:
              return iter(self._attribute_group)
  
      def __len__(self):
          return len(self._attribute_group)
  
      def _parse(self):
          if self.elem.tag == XSD_ATTRIBUTE_GROUP:
              if self.parent is not None:
                  return  # Skip parsing dummy instances
              try:
                  self.name = get_qname(self.target_namespace, self.elem.attrib['name'])
              except KeyError:
                  return
              else:
                  if self.schema.default_attributes == self.name and self.xsd_version > '1.0':
                      self.schema.default_attributes = self
  
          any_attribute = None
          attribute_group_refs = []
          attributes: Dict[Optional[str], Union[XsdAttribute, XsdAnyAttribute]] = {}
  
          for child in self.elem:
              if child.tag == XSD_ANNOTATION or callable(child.tag):
                  continue  # pragma: no cover
              elif any_attribute is not None:
                  if child.tag == XSD_ANY_ATTRIBUTE:
                      self.parse_error("more anyAttribute declarations in the same attribute group")
                  elif child.tag != XSD_ASSERT:
                      self.parse_error("another declaration after anyAttribute")
  
              elif child.tag == XSD_ANY_ATTRIBUTE:
                  any_attribute = self.schema.BUILDERS.any_attribute_class(child, self.schema, self)
                  if None in attributes:
                      attributes[None] = attributes[None].copy()
                      attributes[None].intersection(any_attribute)
                  else:
                      attributes[None] = any_attribute
  
              elif child.tag == XSD_ATTRIBUTE:
                  attribute = self.schema.BUILDERS.attribute_class(child, self.schema, self)
                  if attribute.name in attributes:
                      self.parse_error("multiple declaration for attribute "
                                       "{!r}".format(attribute.name))
                  elif attribute.use != 'prohibited' or self.elem.tag != XSD_ATTRIBUTE_GROUP:
                      attributes[attribute.name] = attribute
  
              elif child.tag == XSD_ATTRIBUTE_GROUP:
                  try:
                      ref = child.attrib['ref']
                  except KeyError:
                      self.parse_error("the attribute 'ref' is required "
                                       "in a local attributeGroup")
                      continue
  
                  try:
                      attribute_group_qname = self.schema.resolve_qname(ref)
                  except (KeyError, ValueError, RuntimeError) as err:
                      self.parse_error(err)
                  else:
                      if attribute_group_qname in attribute_group_refs:
                          self.parse_error("duplicated attributeGroup %r" % ref)
                      elif self.redefine is not None:
                          if attribute_group_qname == self.name:
                              if attribute_group_refs:
                                  self.parse_error("in a redefinition the reference "
                                                   "to itself must be the first")
                              attribute_group_refs.append(attribute_group_qname)
                              attributes.update(self._attribute_group.items())
                              continue
                          elif not attribute_group_refs:
                              # May be an attributeGroup restriction with a ref to another group
                              if not any(e.tag == XSD_ATTRIBUTE_GROUP and ref == e.get('ref')
                                         for e in self.redefine.elem):
                                  self.parse_error("attributeGroup ref=%r is not "
                                                   "in the redefined group" % ref)
                      elif attribute_group_qname == self.name and self.xsd_version == '1.0':
                          self.parse_error("Circular attribute groups not allowed in XSD 1.0")
                      attribute_group_refs.append(attribute_group_qname)
  
                      try:
                          base_attrs = self.maps.lookup_attribute_group(attribute_group_qname)
                      except LookupError:
                          self.parse_error("unknown attribute group %r" % child.attrib['ref'])
                      else:
                          if not isinstance(base_attrs, tuple):
                              for name, attr in base_attrs.items():
                                  if name not in attributes:
                                      attributes[name] = attr
                                  elif name is not None:
                                      self.parse_error("multiple declaration for attribute "
                                                       "{!r}".format(name))
                                  else:
                                      attributes[None] = attributes[None].copy()
                                      attributes[None].intersection(attr)
  
                          elif self.xsd_version == '1.0':
                              self.parse_error(
                                  "Circular reference found between attribute groups "
                                  "{!r} and {!r}".format(self.name, attribute_group_qname)
                              )
  
              elif self.name is not None:
                  self.parse_error("(attribute | attributeGroup) expected, found %r." % child)
  
          # Check and copy base attributes
          if self.base_attributes is not None:
              wildcard = self.base_attributes.get(None)
              for name, attr in attributes.items():
                  if name not in self.base_attributes:
                      if self.derivation != 'restriction':
                          continue
                      elif wildcard is None or not wildcard.is_matching(name, self.default_namespace):
                          self.parse_error("Unexpected attribute %r in restriction" % name)
                      continue
  
                  base_attr = self.base_attributes[name]
  
                  if name is None:
                      if self.derivation == 'extension':
                          try:
                              attr.union(base_attr)
                          except ValueError as err:
                              self.parse_error(err)
                      elif not attr.is_restriction(base_attr):
                          self.parse_error("Attribute wildcard is not a restriction "
                                           "of the base wildcard")
                      continue
                  if self.derivation == 'restriction' and attr.type.name != XSD_ANY_SIMPLE_TYPE and \
                          not attr.type.is_derived(base_attr.type, 'restriction'):
                      self.parse_error("Attribute type is not a restriction "
                                       "of the base attribute type")
                  if base_attr.use != 'optional' and attr.use == 'optional' or \
                          base_attr.use == 'required' and attr.use != 'required':
                      self.parse_error("Attribute %r: unmatched attribute use in restriction" % name)
                  if base_attr.fixed is not None and \
                          attr.type.normalize(attr.fixed) != \
                          base_attr.type.normalize(base_attr.fixed):
                      self.parse_error("Attribute %r: derived attribute "
                                       "has a different fixed value" % name)
                  if base_attr.inheritable is not attr.inheritable:
                      msg = "Attribute %r: attribute 'inheritable' value change in restriction"
                      self.parse_error(msg % name)
  
              if self.redefine is not None:
                  pass  # In case of redefinition do not copy base attributes
              else:
                  self._attribute_group.update(self.base_attributes.items())
  
          elif self.redefine is not None and not attribute_group_refs:
              for name, attr in self._attribute_group.items():
                  if name is None:
                      continue
                  elif name not in attributes:
                      if attr.use == 'required':
                          self.parse_error("Missing required attribute %r in "
                                           "redefinition restriction" % name)
                      continue
                  if attr.use != 'optional' and attributes[name].use != attr.use:
                      self.parse_error("Attribute %r: unmatched attribute use in redefinition" % name)
                  if attr.fixed is not None and attributes[name].fixed is None:
                      self.parse_error("Attribute %r: redefinition remove fixed constraint" % name)
  
              pos = 0
              keys = list(self._attribute_group.keys())
              for name in attributes:
                  try:
                      next_pos = keys.index(name)
                  except ValueError:
                      self.parse_error("Redefinition restriction contains "
                                       "additional attribute %r" % name)
                  else:
                      if next_pos < pos:
                          self.parse_error("Wrong attribute order in redefinition restriction")
                          break
                      pos = next_pos
              self.clear()
  
          self._attribute_group.update(attributes)
          if None in self._attribute_group and None not in attributes \
                  and self.derivation == 'restriction':
              wildcard = self._attribute_group[None].copy()
              wildcard.namespace = wildcard.not_namespace = wildcard.not_qname = ()
              self._attribute_group[None] = wildcard
  
          if self.xsd_version == '1.0':
              has_key = False
              for attr in self._attribute_group.values():
                  if attr.name and attr.type.is_key():
                      if has_key:
                          self.parse_error("multiple ID attributes not allowed for XSD 1.0")
                          break
                      has_key = True
  
          elif self.parent is None and self.schema.default_attributes == self.name:
              self.schema.default_attributes = self
  
      @property
      def built(self):
          return True
  
      def parse_error(self, error, elem=None, validation=None):
          if self.parent is None:
              super().parse_error(error, elem, validation)
          else:
              self.parent.parse_error(error, elem, validation)
  
      def iter_required(self):
          for k, v in self._attribute_group.items():
              if k is not None and v.use == 'required':
                  yield k
  
      def iter_value_constraints(self, use_defaults=True):
          if use_defaults:
              for k, v in self._attribute_group.items():
                  if not k:
                      continue
                  elif v.fixed is not None:
                      yield k, v.fixed
                  elif v.default is not None:
                      yield k, v.default
          else:
              for k, v in self._attribute_group.items():
                  if k and v.fixed is not None:
                      yield k, v.fixed
  
      def iter_components(self, xsd_classes=None):
          if xsd_classes is None or isinstance(self, xsd_classes):
              yield self
  
          for attr in self.values():
              if attr.parent is not None:
                  yield from attr.iter_components(xsd_classes)
  
      def iter_decode(self, attrs, validation='lax', **kwargs):
          if not attrs and not self:
              return
  
          for name in filter(lambda x: x not in attrs, self.iter_required()):
              reason = "missing required attribute {!r}".format(name)
              yield self.validation_error(validation, reason, attrs, **kwargs)
  
          kwargs['level'] = kwargs.get('level', 0) + 1
          try:
              use_defaults = kwargs['use_defaults']
          except KeyError:
              use_defaults = True
  
          additional_attrs = [
              (k, v) for k, v in self.iter_value_constraints(use_defaults) if k not in attrs
          ]
          if additional_attrs:
              attrs = {k: v for k, v in attrs.items()}
              attrs.update(additional_attrs)
  
          if self.xsd_version == '1.0':
              kwargs['id_list'] = []
  
          filler = kwargs.get('filler')
          result_list = []
          for name, value in attrs.items():
              try:
                  xsd_attribute = self._attribute_group[name]
              except KeyError:
                  if get_namespace(name) == XSI_NAMESPACE:
                      try:
                          xsd_attribute = self.maps.lookup_attribute(name)
                      except LookupError:
                          try:
                              xsd_attribute = self._attribute_group[None]  # None == anyAttribute
                              value = (name, value)
                          except KeyError:
                              reason = "%r is not an attribute of the XSI namespace." % name
                              yield self.validation_error(validation, reason, attrs, **kwargs)
                              continue
                  else:
                      try:
                          xsd_attribute = self._attribute_group[None]  # None == anyAttribute
                          value = (name, value)
                      except KeyError:
                          reason = "%r attribute not allowed for element." % name
                          yield self.validation_error(validation, reason, attrs, **kwargs)
                          continue
              else:
                  if xsd_attribute.use == 'prohibited' and \
                          (None not in self or not self._attribute_group[None].is_matching(name)):
                      reason = "use of attribute %r is prohibited" % name
                      yield self.validation_error(validation, reason, attrs, **kwargs)
  
              for result in xsd_attribute.iter_decode(value, validation, **kwargs):
                  if isinstance(result, XMLSchemaValidationError):
                      yield result
                  elif result is None and filler is not None:
                      result_list.append((name, filler(xsd_attribute)))
                      break
                  else:
                      result_list.append((name, result))
                      break
  
          if kwargs.get('fill_missing'):
              if filler is None:
                  result_list.extend((k, None) for k in self._attribute_group
                                     if k is not None and k not in attrs)
              else:
                  result_list.extend((k, filler(v)) for k, v in self._attribute_group.items()
                                     if k is not None and k not in attrs)
  
          yield result_list
  
      def iter_encode(self, attrs, validation='lax', **kwargs):
          if not attrs and not self:
              return
  
          for name in filter(lambda x: x not in attrs, self.iter_required()):
              reason = "missing required attribute {!r}".format(name)
              yield self.validation_error(validation, reason, attrs, **kwargs)
  
          try:
              use_defaults = kwargs['use_defaults']
          except KeyError:
              use_defaults = True
  
          result_list = []
          for name, value in attrs.items():
              try:
                  xsd_attribute = self._attribute_group[name]
              except KeyError:
                  namespace = get_namespace(name) or self.target_namespace
                  if namespace == XSI_NAMESPACE:
                      try:
                          xsd_attribute = self.maps.lookup_attribute(name)
                      except LookupError:
                          try:
                              xsd_attribute = self._attribute_group[None]  # None == anyAttribute
                              value = (name, value)
                          except KeyError:
                              reason = "%r is not an attribute of the XSI namespace." % name
                              yield self.validation_error(validation, reason, attrs, **kwargs)
                              continue
                  else:
                      try:
                          xsd_attribute = self._attribute_group[None]  # None == anyAttribute
                          value = (name, value)
                      except KeyError:
                          reason = "%r attribute not allowed for element." % name
                          yield self.validation_error(validation, reason, attrs, **kwargs)
                          continue
  
              for result in xsd_attribute.iter_encode(value, validation, **kwargs):
                  if isinstance(result, XMLSchemaValidationError):
                      yield result
                  else:
                      if result is not None:
                          result_list.append((name, result))
                      break
  
          result_list.extend(
              (k, v) for k, v in self.iter_value_constraints(use_defaults) if k not in attrs
          )
          yield result_list
  