<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def convert_to_base_types(obj, ignore_keys=tuple(), tuple_type=tuple, json_safe=True):
 """Recursively convert objects into base types. This is used to convert some special types of objects used internally into base types for more friendly output via mechanisms such as JSON. It is used for sending internal objects via the network and outputting test records. Specifically, the conversions that are performed: - If an object has an as_base_types() method, immediately return the result without any recursion; this can be used with caching in the object to prevent unnecessary conversions. - If an object has an _asdict() method, use that to convert it to a dict and recursively converting its contents. - mutablerecords Record instances are converted to dicts that map attribute name to value. Optional attributes with a value of None are skipped. - Enum instances are converted to strings via their .name attribute. - Real and integral numbers are converted to built-in types. - Byte and unicode strings are left alone (instances of six.string_types). - Other non-None values are converted to strings via str(). The return value contains only the Python built-in types: dict, list, tuple, str, unicode, int, float, long, bool, and NoneType (unless tuple_type is set to something else). If tuples should be converted to lists (e.g. for an encoding that does not differentiate between the two), pass 'tuple_type=list' as an argument. If `json_safe` is True, then the float 'inf', '-inf', and 'nan' values will be converted to strings. This ensures that the returned dictionary can be passed to json.dumps to create valid JSON. Otherwise, json.dumps may return values such as NaN which are not valid JSON. """

  # Because it's *really* annoying to pass a single string accidentally.
  assert not isinstance(ignore_keys, six.string_types), 'Pass a real iterable!'

  if hasattr(obj, 'as_base_types'):
    return obj.as_base_types()
  if hasattr(obj, '_asdict'):
    obj = obj._asdict()
  elif isinstance(obj, records.RecordClass):
    obj = {attr: getattr(obj, attr)
           for attr in type(obj).all_attribute_names
           if (getattr(obj, attr, None) is not None or
               attr in type(obj).required_attributes)}
  elif isinstance(obj, Enum):
    obj = obj.name

  if type(obj) in PASSTHROUGH_TYPES:
    return obj

  # Recursively convert values in dicts, lists, and tuples.
  if isinstance(obj, dict):
    return {convert_to_base_types(k, ignore_keys, tuple_type):
               convert_to_base_types(v, ignore_keys, tuple_type)
            for k, v in six.iteritems(obj) if k not in ignore_keys}
  elif isinstance(obj, list):
    return [convert_to_base_types(val, ignore_keys, tuple_type, json_safe)
            for val in obj]
  elif isinstance(obj, tuple):
    return tuple_type(
        convert_to_base_types(value, ignore_keys, tuple_type, json_safe)
        for value in obj)

  # Convert numeric types (e.g. numpy ints and floats) into built-in types.
  elif isinstance(obj, numbers.Integral):
    return long(obj)
  elif isinstance(obj, numbers.Real):
    as_float = float(obj)
    if json_safe and (math.isinf(as_float) or math.isnan(as_float)):
      return str(as_float)
    return as_float

  # Convert all other types to strings.
  try:
    return str(obj)
  except:
    logging.warning('Problem casting object of type %s to str.', type(obj))
    raise