docs: def read(self, fh, api_version=None):
          """Read info.json from file like object.
  
          Parameters:
          fh -- file like object supporting fh.read()
          api_version -- IIIF Image API version expected
  
          If api_version is set then the parsing will assume this API version,
          else the version will be determined from the incoming data. NOTE that
          the value of self.api_version is NOT used in this routine.
          If an api_version is specified and there is a @context specified then
          an IIIFInfoError will be raised unless these match. If no known
          @context is present and no api_version set then an IIIFInfoError
          will be raised.
          """
          j = json.load(fh)
          #
          # @context and API version
          self.context = None
          if (api_version == '1.0'):
              # v1.0 did not have a @context so we simply take the version
              # passed in
              self.api_version = api_version
          elif ('@context' in j):
              # determine API version from context
              self.context = j['@context']
              api_version_read = None
              for v in CONF:
                  if (v > '1.0' and self.context == CONF[v]['context']):
                      api_version_read = v
                      break
              if (api_version_read is None):
                  raise IIIFInfoError(
                      "Unknown @context, cannot determine API version (%s)" %
                      (self.context))
              else:
                  if (api_version is not None and
                          api_version != api_version_read):
                      raise IIIFInfoError(
                          "Expected API version '%s' but got @context for API version '%s'" %
                          (api_version, api_version_read))
                  else:
                      self.api_version = api_version_read
          else:  # no @context and not 1.0
              if (api_version is None):
                  raise IIIFInfoError("No @context (and no default given)")
              self.api_version = api_version
          self.set_version_info()
          #
          # @id or identifier
          if (self.api_version == '1.0'):
              if ('identifier' in j):
                  self.id = j['identifier']
              else:
                  raise IIIFInfoError("Missing identifier in info.json")
          else:
              if ('@id' in j):
                  self.id = j['@id']
              else:
                  raise IIIFInfoError("Missing @id in info.json")
          #
          # other params
          for param in self.params:
              if (param == 'identifier'):
                  continue  # dealt with above
              if (param in j):
                  if (param in self.complex_params):
                      # use function ref in complex_params to parse, optional
                      # dst to map to a different property name
                      self._setattr(param, self.complex_params[
                                    param](self, j[param]))
                  else:
                      self._setattr(param, j[param])
          return True