code: def _get_cascading_attr(self, attr_name, *args):
          
          attr = self._data.get(attr_name, None)
          if isinstance(attr, ConfigDict):
              attr = ConfigDict(attr, attr.start_mark, attr.end_mark)
          elif isinstance(attr, dict):
              attr = attr.copy()
  
          cascading = self._data
          if args:
  
              for key in args:
                  if not isinstance(key, six.string_types):
                      raise TypeError('Every key in args must be a string.')
  
                  if key in cascading:
                      config_object = cascading.get(key)
  
                      if config_object:
                          if attr_name in config_object:
                              if isinstance(attr, dict):
                                  value = config_object[attr_name]
  
                                  if value is None:
                                      attr = value
                                  elif isinstance(value, dict):
                                      attr.update(config_object[attr_name])
                                  else:
                                      raise LookupError(
                                          "Unable to find '{0}'. Obj structure: {1}".format(
                                              attr_name, " -> ".join(args)
                                          )
                                      )
                              else:
                                  attr = config_object[attr_name]
  
                          cascading = config_object
                          continue
                  else:
                      break
  
          return attr
docs: Will traverse the configuration data looking for attr_name provided.  It will know where to look for the
          attribute based on the *args that are passed to the method.  It treats the args as ancestors starting with the
          oldest `root -> grandparent -> parent -> attr_name`.  The findings will be updated with the last found object.
          The properties in the last found object will overwrite those in the previous.  If the attribute isn't found
          will return None
  
          Usage::
              >>> attr = self._get_cascading_attr('attribute_name', 'root', 'grandparent', 'parent')
              >>> if attr is not None:
              >>>  do_thing(attr)
  
          :param attr_name: A string, the configuration attribute name
          :param *args: A list, A list of strings that that represent the attributes ancestry. (how to find the obj)
          :rtype: Any defined configuration value :dict:, :string:, :int:, :list:, :float: `attr`