class_docstr: A `Dataset` comprising records from one or more Avro files.
  
  This dataset load Avro records from the files into a dict of tensors.
  The output dict has feature name as key and tf.Tensor or tf.SparseTensor
  as value. The output tensor values are batched with the user defined
  batch size.
  
  Shuffle can be enabled before batch by configuring shuffle buffer size.
  The shuffle buffer size dictates the elements *in addition* to the batch size
  that would be read and sampled.
  This dataset keeps collecting Avro blocks(a sequence of Avro records),
  until the total number of unread records is greater than the shuffle
  buffer size + batch_size, then randomly samples block from the collected blocks.
  An Avro Record from the sampled block will be parsed and batched into
  the output tensors.
  
  For instance, assume your dataset contains 5 blocks with 100 records in
  each block. When the batch size is set to 32 and shuffle buffer size is set
  to 128, this dataset will collect two blocks as the two blocks contains more
  than 128 + 32 = 160 unread records, and randomly samples block from the two
  blocks 32 times.
  When a block is sampled, a record in the sampled block is read and batched
  into the output tensor dict until all records in the sampled block are read.
  If only one block fits into the batch + shuffle_buffer_size, records in that
  block will be read sequentially without shuffle. Users can increase the
  shuffle buffer size or apply dataset unbatch, shuffle, and batch for better shuffling.
  
  The memory footprint of this shuffle buffer is signficantly different from tf.data.Dataset.shuffle
  In Tensorflow's shuffle, the shuffle buffer specifies a separate buffer of elements to pick
  random elements from. In this implementation, the shuffle buffer + batch forms the total number of
  elements that would be read for sampling.
  
  Here's an example comparing shuffle between AvroReader and Tensorflow:
  
  Data size is 1000
  Batch size is 64
  
  Case 1: perfect shuffle
  Shuffle buffer size 1000
  TF: shuffle elements(1, 1000) to create a batch of size 64
  AvroReader: shuffle elements(1, 1000) to create a batch of size 64
  
  Case 2: not perfect but shuffle > batch
  Shuffle buffer size is 256
  TF:
  Shuffle elements(1, 256) to pick 1 element
  Shuffle elements(1, 257) to pick 1 element
  …
  shuffle(1, 320) to create a batch of size 64
  
  AvroReader:
  Shuffle elements(1, 320) to create a batch of size 64
  
  Case 3: shuffle buffer < batch
  Shuffle buffer size is 32
  TF:
  Shuffle elements(1, 32) to pick 1 element
  Shuffle elements(1, 33) to pick 1 element
  …
  shuffle(1, 96) to create a batch of size 64
  
  ATDS: Shuffle elements(1, 96) to create a batch of size 64
  
  Case 4: no shuffle
  Shuffle buffer size is 0
  Tensorflow and ATDS both will just directly read to create a batch of size 64
  
  
  A minimal example is given below:
  
  >>> import tempfile
  >>> import avro.schema
  >>> from avro.datafile import DataFileWriter
  >>> from avro.io import DatumWriter
  >>> from tensorflow_io.python.experimental.atds.dataset import ATDSDataset
  >>> from tensorflow_io.python.experimental.atds.features import DenseFeature
  >>> example_path = os.path.join(tempfile.gettempdir(), "example.avro")
  >>> np.random.seed(0)
  
  >>> # Define Avro schema in ATDS format.
  >>> json_schema = '''{
  ...     "type": "record",
  ...     "name": "example",
  ...     "fields": [
  ...         { "name": "x", "type": "float" },
  ...         { "name": "y", "type": "float" }
  ...     ]
  ... }'''
  >>> schema = avro.schema.Parse(json_schema)
  
  >>> # Write the Avro records to a file.
  >>> with open(example_path, "wb") as f:
  ...     writer = DataFileWriter(f, DatumWriter(), schema)
  ...     for _ in range(3):
  ...         x, y = np.random.random(), np.random.random()
  ...         writer.append({"x": x, "y": y})
  ...     writer.close()
  
  >>> # Read the data back out.
  >>> feature_config = {
  ...     "x": DenseFeature([], dtype=tf.float32),
  ...     "y": DenseFeature([], dtype=tf.float32)
  ... }
  >>> for batch in ATDSDataset([example_path], batch_size=2,
  ...                         features=feature_config):
  ...     print("x = {x},  y = {y}".format(**batch))
  x = [0.5488135  0.60276335],  y = [0.71518934 0.5448832 ]
  x = [0.4236548],  y = [0.6458941]
code: def element_spec(self):
      return self._element_spec
  
file_path: io/tensorflow_io/python/experimental/atds/dataset.py
imports: from typing import Optional
  import tensorflow
  from tensorflow.python.data.util import convert
  from tensorflow.python.data.util import structure
  from tensorflow.python.data.util import nest
  from tensorflow.python.data.ops import dataset_ops
  from tensorflow.python.framework import errors
  from tensorflow.python.framework import tensor_util
  from tensorflow_io.python.ops import core_ops
  from tensorflow_io.python.experimental.atds.features import DenseFeature, SparseFeature, VarlenFeature
label_desc: Use image node_id 2 for calling the ATDSDataset obj's underlying member method code with example usage: obj.element_spec() and returns: self