text: # Licensed under a 3-clause BSD style license - see LICENSE.rst
  #
  # NOTE: docstring is in docs/io/misc.rst because of
  # https://github.com/astropy/pytest-doctestplus/issues/109
  
  import base64
  
  import numpy as np
  
  from astropy.time import Time, TimeDelta
  from astropy import units as u
  from astropy import coordinates as coords
  from astropy.utils import minversion
  from astropy.table import SerializedColumn
  
  try:
      import yaml
  except ImportError:
      YAML_LT_3_12 = True
  
      class yaml:  # hacky not-quite-a-duck-type
          SafeLoader = object
          SafeDumper = object
  
  else:
      YAML_LT_3_12 = not minversion(yaml, '3.12')
  
  __all__ = ['AstropyLoader', 'AstropyDumper', 'load', 'load_all', 'dump']
  __doctest_requires__ = {'*': ['yaml']}
  
  
  def _unit_representer(dumper, obj):
      out = {'unit': str(obj.to_string())}
      return dumper.represent_mapping('!astropy.units.Unit', out)
  
  
  def _unit_constructor(loader, node):
      map = loader.construct_mapping(node)
      return u.Unit(map['unit'], parse_strict='warn')
  
  
  def _serialized_column_representer(dumper, obj):
      out = dumper.represent_mapping('!astropy.table.SerializedColumn', obj)
      return out
  
  
  def _serialized_column_constructor(loader, node):
      map = loader.construct_mapping(node)
      return SerializedColumn(map)
  
  
  def _time_representer(dumper, obj):
      out = obj.info._represent_as_dict()
      return dumper.represent_mapping('!astropy.time.Time', out)
  
  
  def _time_constructor(loader, node):
      map = loader.construct_mapping(node)
      out = Time.info._construct_from_dict(map)
      return out
  
  
  def _timedelta_representer(dumper, obj):
      out = obj.info._represent_as_dict()
      return dumper.represent_mapping('!astropy.time.TimeDelta', out)
  
  
  def _timedelta_constructor(loader, node):
      map = loader.construct_mapping(node)
      out = TimeDelta.info._construct_from_dict(map)
      return out
  
  
  def _ndarray_representer(dumper, obj):
      if not (obj.flags['C_CONTIGUOUS'] or obj.flags['F_CONTIGUOUS']):
          obj = np.ascontiguousarray(obj)
  
      if np.isfortran(obj):
          obj = obj.T
          order = 'F'
      else:
          order = 'C'
  
      data_b64 = base64.b64encode(obj.tobytes())
  
      out = dict(buffer=data_b64,
                 dtype=str(obj.dtype),
                 shape=obj.shape,
                 order=order)
  
      return dumper.represent_mapping('!numpy.ndarray', out)
  
  
  def _ndarray_constructor(loader, node):
      map = loader.construct_mapping(node)
      map['buffer'] = base64.b64decode(map['buffer'])
      return np.ndarray(**map)
  
  
  def _quantity_representer(tag):
      def representer(dumper, obj):
          out = obj.info._represent_as_dict()
          return dumper.represent_mapping(tag, out)
      return representer
  
  
  def _quantity_constructor(cls):
      def constructor(loader, node):
          map = loader.construct_mapping(node)
          return cls.info._construct_from_dict(map)
      return constructor
  
  
  def _skycoord_representer(dumper, obj):
      map = obj.info._represent_as_dict()
      out = dumper.represent_mapping('!astropy.coordinates.sky_coordinate.SkyCoord',
                                     map)
      return out
  
  
  def _skycoord_constructor(loader, node):
      map = loader.construct_mapping(node)
      out = coords.SkyCoord.info._construct_from_dict(map)
      return out
  
  
  # Straight from yaml's Representer
  def _complex_representer(self, data):
      if data.imag == 0.0:
          data = '%r' % data.real
      elif data.real == 0.0:
          data = '%rj' % data.imag
      elif data.imag > 0:
          data = f'{data.real!r}+{data.imag!r}j'
      else:
          data = f'{data.real!r}{data.imag!r}j'
      return self.represent_scalar('tag:yaml.org,2002:python/complex', data)
  
  
  def _complex_constructor(loader, node):
      map = loader.construct_scalar(node)
      return complex(map)
  
  
  class AstropyLoader(yaml.SafeLoader):
      """
      Custom SafeLoader that constructs astropy core objects as well
      as Python tuple and unicode objects.
  
      This class is not directly instantiated by user code, but instead is
      used to maintain the available constructor functions that are
      called when parsing a YAML stream.  See the `PyYaml documentation
      <https://pyyaml.org/wiki/PyYAMLDocumentation>`_ for details of the
      class signature.
      """
      def __init__(self, *args, **kwargs):
          if YAML_LT_3_12:
              raise ImportError('`import yaml` failed, PyYAML package is required for YAML and must be 3.12 or later')
  
          super().__init__(*args, **kwargs)
  
      def _construct_python_tuple(self, node):
          return tuple(self.construct_sequence(node))
  
      def _construct_python_unicode(self, node):
          return self.construct_scalar(node)
  
  
  class AstropyDumper(yaml.SafeDumper):
      """
      Custom SafeDumper that represents astropy core objects as well
      as Python tuple and unicode objects.
  
      This class is not directly instantiated by user code, but instead is
      used to maintain the available representer functions that are
      called when generating a YAML stream from an object.  See the
      `PyYaml documentation <https://pyyaml.org/wiki/PyYAMLDocumentation>`_
      for details of the class signature.
      """
      def __init__(self, *args, **kwargs):
          if YAML_LT_3_12:
              raise ImportError('`import yaml` failed, PyYAML package is required for YAML and must be 3.12 or later')
  
          super().__init__(*args, **kwargs)
  
      def _represent_tuple(self, data):
          return self.represent_sequence('tag:yaml.org,2002:python/tuple', data)
  
  
  if not YAML_LT_3_12:
      AstropyDumper.add_multi_representer(u.UnitBase, _unit_representer)
      AstropyDumper.add_multi_representer(u.FunctionUnitBase, _unit_representer)
      AstropyDumper.add_representer(tuple, AstropyDumper._represent_tuple)
      AstropyDumper.add_representer(np.ndarray, _ndarray_representer)
      AstropyDumper.add_representer(Time, _time_representer)
      AstropyDumper.add_representer(TimeDelta, _timedelta_representer)
      AstropyDumper.add_representer(coords.SkyCoord, _skycoord_representer)
      AstropyDumper.add_representer(SerializedColumn, _serialized_column_representer)
  
      # Numpy dtypes
      AstropyDumper.add_representer(np.bool_, yaml.representer.SafeRepresenter.represent_bool)
      for np_type in [np.int_, np.intc, np.intp, np.int8, np.int16, np.int32,
                      np.int64, np.uint8, np.uint16, np.uint32, np.uint64]:
          AstropyDumper.add_representer(np_type,
                                        yaml.representer.SafeRepresenter.represent_int)
      for np_type in [np.float_, np.float16, np.float32, np.float64,
                      np.longdouble]:
          AstropyDumper.add_representer(np_type,
                                        yaml.representer.SafeRepresenter.represent_float)
      for np_type in [np.complex_, complex, np.complex64, np.complex128]:
          AstropyDumper.add_representer(np_type, _complex_representer)
  
      AstropyLoader.add_constructor('tag:yaml.org,2002:python/complex',
                                    _complex_constructor)
      AstropyLoader.add_constructor('tag:yaml.org,2002:python/tuple',
                                    AstropyLoader._construct_python_tuple)
      AstropyLoader.add_constructor('tag:yaml.org,2002:python/unicode',
                                    AstropyLoader._construct_python_unicode)
      AstropyLoader.add_constructor('!astropy.units.Unit', _unit_constructor)
      AstropyLoader.add_constructor('!numpy.ndarray', _ndarray_constructor)
      AstropyLoader.add_constructor('!astropy.time.Time', _time_constructor)
      AstropyLoader.add_constructor('!astropy.time.TimeDelta', _timedelta_constructor)
      AstropyLoader.add_constructor('!astropy.coordinates.sky_coordinate.SkyCoord',
                                    _skycoord_constructor)
      AstropyLoader.add_constructor('!astropy.table.SerializedColumn',
                                    _serialized_column_constructor)
  
      for cls, tag in ((u.Quantity, '!astropy.units.Quantity'),
                       (u.Magnitude, '!astropy.units.Magnitude'),
                       (u.Dex, '!astropy.units.Dex'),
                       (u.Decibel, '!astropy.units.Decibel'),
                       (coords.Angle, '!astropy.coordinates.Angle'),
                       (coords.Latitude, '!astropy.coordinates.Latitude'),
                       (coords.Longitude, '!astropy.coordinates.Longitude'),
                       (coords.EarthLocation, '!astropy.coordinates.earth.EarthLocation')):
          AstropyDumper.add_multi_representer(cls, _quantity_representer(tag))
          AstropyLoader.add_constructor(tag, _quantity_constructor(cls))
  
      for cls in (list(coords.representation.REPRESENTATION_CLASSES.values())
                  + list(coords.representation.DIFFERENTIAL_CLASSES.values())):
          name = cls.__name__
          # Add representations/differentials defined in astropy.
          if name in coords.representation.__all__:
              tag = '!astropy.coordinates.' + name
              AstropyDumper.add_multi_representer(cls, _quantity_representer(tag))
              AstropyLoader.add_constructor(tag, _quantity_constructor(cls))
  
  
  def load(stream):
      """Parse the first YAML document in a stream using the AstropyLoader and
      produce the corresponding Python object.
  
      Parameters
      ----------
      stream : str or file-like object
          YAML input
  
      Returns
      -------
      obj : object
          Object corresponding to YAML document
      """
      if YAML_LT_3_12:
          raise ImportError('`import yaml` failed, PyYAML package is required '
                            'for YAML and must be 3.12 or later')
      return yaml.load(stream, Loader=AstropyLoader)
  
  
  def load_all(stream):
      """Parse the all YAML documents in a stream using the AstropyLoader class and
      produce the corresponding Python object.
  
      Parameters
      ----------
      stream : str or file-like object
          YAML input
  
      Returns
      -------
      obj : object
          Object corresponding to YAML document
  
      """
      if YAML_LT_3_12:
          raise ImportError('`import yaml` failed, PyYAML package is required '
                            'for YAML and must be 3.12 or later')
      return yaml.load_all(stream, Loader=AstropyLoader)
  
  
  def dump(data, stream=None, **kwargs):
      """Serialize a Python object into a YAML stream using the AstropyDumper class.
      If stream is None, return the produced string instead.
  
      Parameters
      ----------
      data: object
          Object to serialize to YAML
      stream : file-like object, optional
          YAML output (if not supplied a string is returned)
      **kwargs
          Other keyword arguments that get passed to yaml.dump()
  
      Returns
      -------
      out : str or None
          If no ``stream`` is supplied then YAML output is returned as str
  
      """
      if YAML_LT_3_12:
          raise ImportError('`import yaml` failed, PyYAML package is required '
                            'for YAML and must be 3.12 or later')
      kwargs['Dumper'] = AstropyDumper
      kwargs.setdefault('default_flow_style', None)
      return yaml.dump(data, stream=stream, **kwargs)
  