docs: def parse_limit_node(db, idx, limit):
      """
      Given an XML node describing a limit, return a Limit object.
  
      :param db: Handle for the Redis database.
      :param idx: The index of the limit in the XML file; used for error
                  reporting.
      :param limit: The XML node describing the limit.
      """
  
      # First, try to import the class; this will raise ImportError if
      # we can't import it
      klass = utils.find_entrypoint('turnstile.limit', limit.get('class'),
                                    required=True)
  
      # Build the list of required attributes
      required = set(k for k, v in klass.attrs.items()
                     if 'default' not in v)
  
      # Now, use introspection on the class to interpret the attributes
      attrs = {}
      for child in limit:
          # Basic validation of child elements
          if child.tag != 'attr':
              warnings.warn("Unrecognized element %r while parsing limit at "
                            "index %d; ignoring..." % (child.tag, idx))
              continue
  
          # Get the attribute name
          attr = child.get('name')
  
          # Be liberal in what we accept--ignore unrecognized attributes
          # (with a warning)
          if attr not in klass.attrs:
              warnings.warn("Limit at index %d does not accept an attribute "
                            "%r; ignoring..." % (idx, attr))
              continue
  
          # OK, get the attribute descriptor
          desc = klass.attrs[attr]
  
          # Grab the attribute type
          attr_type = desc.get('type', str)
  
          if attr_type == list:
              # Lists are expressed as child elements; we ignore the
              # child element names
              subtype = desc.get('subtype', str)
              value = []
              try:
                  for j, grandchild in enumerate(child):
                      if grandchild.tag != 'value':
                          warnings.warn("Unrecognized element %r while parsing "
                                        "%r attribute of limit at index %d; "
                                        "ignoring element..." %
                                        (grandchild.tag, attr, idx))
                          continue
  
                      value.append(subtype(grandchild.text))
              except ValueError:
                  warnings.warn("Invalid value %r while parsing element %d "
                                "of %r attribute of limit at index %d; "
                                "ignoring attribute..." %
                                (grandchild.text, j, attr, idx))
                  continue
          elif attr_type == dict:
              # Dicts are expressed as child elements, with the tags
              # identifying the attribute name
              subtype = desc.get('subtype', str)
              value = {}
              for grandchild in child:
                  if grandchild.tag != 'value':
                      warnings.warn("Unrecognized element %r while parsing "
                                    "%r attribute of limit at index %d; "
                                    "ignoring element..." %
                                    (grandchild.tag, attr, idx))
                      continue
                  elif 'key' not in grandchild.attrib:
                      warnings.warn("Missing 'key' attribute of 'value' "
                                    "element while parsing %r attribute of "
                                    "limit at index %d; ignoring element..." %
                                    (attr, idx))
                      continue
  
                  try:
                      value[grandchild.get('key')] = subtype(grandchild.text)
                  except ValueError:
                      warnings.warn("Invalid value %r while parsing %r element "
                                    "of %r attribute of limit at index %d; "
                                    "ignoring element..." %
                                    (grandchild.text, grandchild.get('key'),
                                     attr, idx))
                      continue
          elif attr_type == bool:
              try:
                  value = config.Config.to_bool(child.text)
              except ValueError:
                  warnings.warn("Unrecognized boolean value %r while parsing "
                                "%r attribute of limit at index %d; "
                                "ignoring..." % (child.text, attr, idx))
                  continue
          else:
              # Simple type conversion
              try:
                  value = attr_type(child.text)
              except ValueError:
                  warnings.warn("Invalid value %r while parsing %r attribute "
                                "of limit at index %d; ignoring..." %
                                (child.text, attr, idx))
                  continue
  
          # Save the attribute
          attrs[attr] = value
  
          # Remove from the required set
          required.discard(attr)
  
      # Did we get all required attributes?
      if required:
          raise TypeError("Missing required attributes %s" %
                          (', '.join(repr(a) for a in sorted(required))))
  
      # OK, instantiate and return the class
      return klass(db, **attrs)