class_docstr: Interface for all readers.
  
  This class contains a mapping of file extensions / Reader classes, to know
  which Reader class must be used to read a file (based on its extension).
  This is customizable both with the 'READERS' setting, and with the
  'readers_init' signall for plugins.
class_docstr_tok: ["Interface","for","all","readers",".","This","class","contains","a","mapping","of","file","extensions","\/","Reader","classes",",","to","know","which","Reader","class","must","be","used","to","read","a","file","(","based","on","its","extension",")",".","This","is","customizable","both","with","the","'READERS","'","setting",",","and","with","the","'readers_init","'","signall","for","plugins","."]
docstr: Return a content object parsed with the given format.
code: def read_file(
      self,
      base_path,
      path,
      content_class=Page,
      fmt=None,
      context=None,
      preread_signal=None,
      preread_sender=None,
      context_signal=None,
      context_sender=None,
  ):
      """Return a content object parsed with the given format."""
  
      path = os.path.abspath(os.path.join(base_path, path))
      source_path = posixize_path(os.path.relpath(path, base_path))
      logger.debug(
          "Read file %s -> %s", source_path, content_class.__name__
      )
  
      if not fmt:
          _, ext = os.path.splitext(os.path.basename(path))
          fmt = ext[1:]
  
      if fmt not in self.readers:
          raise TypeError("Pelican does not know how to parse %s", path)
  
      if preread_signal:
          logger.debug(
              "Signal %s.send(%s)", preread_signal.name, preread_sender
          )
          preread_signal.send(preread_sender)
  
      reader = self.readers[fmt]
  
      metadata = _filter_discardable_metadata(
          default_metadata(
              settings=self.settings, process=reader.process_metadata
          )
      )
      metadata.update(
          path_metadata(
              full_path=path,
              source_path=source_path,
              settings=self.settings,
          )
      )
      metadata.update(
          _filter_discardable_metadata(
              parse_path_metadata(
                  source_path=source_path,
                  settings=self.settings,
                  process=reader.process_metadata,
              )
          )
      )
      reader_name = reader.__class__.__name__
      metadata["reader"] = reader_name.replace("Reader", "").lower()
  
      content, reader_metadata = self.get_cached_data(
          path, (None, None)
      )
      if content is None:
          content, reader_metadata = reader.read(path)
          reader_metadata = _filter_discardable_metadata(
              reader_metadata
          )
          self.cache_data(path, (content, reader_metadata))
      metadata.update(reader_metadata)
  
      if content:
          # find images with empty alt
          find_empty_alt(content, path)
  
      # eventually filter the content with typogrify if asked so
      if self.settings["TYPOGRIFY"]:
          from typogrify.filters import typogrify
          import smartypants
  
          typogrify_dashes = self.settings["TYPOGRIFY_DASHES"]
          if typogrify_dashes == "oldschool":
              smartypants.Attr.default = smartypants.Attr.set2
          elif typogrify_dashes == "oldschool_inverted":
              smartypants.Attr.default = smartypants.Attr.set3
          else:
              smartypants.Attr.default = smartypants.Attr.set1
  
          # Tell `smartypants` to also replace &quot; HTML entities with
          # smart quotes. This is necessary because Docutils has already
          # replaced double quotes with said entities by the time we run
          # this filter.
          smartypants.Attr.default |= smartypants.Attr.w
  
          def typogrify_wrapper(text):
              """Ensures ignore_tags feature is backward compatible"""
              try:
                  return typogrify(
                      text, self.settings["TYPOGRIFY_IGNORE_TAGS"]
                  )
              except TypeError:
                  return typogrify(text)
  
          if content:
              content = typogrify_wrapper(content)
  
          if "title" in metadata:
              metadata["title"] = typogrify_wrapper(metadata["title"])
  
          if "summary" in metadata:
              metadata["summary"] = typogrify_wrapper(
                  metadata["summary"]
              )
  
      if context_signal:
          logger.debug(
              "Signal %s.send(%s, <metadata>)",
              context_signal.name,
              context_sender,
          )
          context_signal.send(context_sender, metadata=metadata)
  
      return content_class(
          content=content,
          metadata=metadata,
          settings=self.settings,
          source_path=path,
          context=context,
      )
  
imports: import datetime
  import logging
  import os
  import re
  from collections import OrderedDict
  from html import escape
  from html.parser import HTMLParser
  from io import StringIO
  import docutils
  import docutils.core
  import docutils.io
  from docutils.parsers.rst.languages import get_language
  from docutils.writers.html4css1 import HTMLTranslator, Writer
  from pelican import rstdirectives
  from pelican.cache import FileStampDataCacher
  from pelican.contents import Author, Category, Page, Tag
  from pelican.plugins import signals
  from pelican.utils import get_date, pelican_open, posixize_path
label_desc: Use image node_id 3 for calling the Readers obj's underlying member method code with example usage: obj.read_file(base_path, path, content_class, fmt, context, preread_signal, preread_sender, context_signal, context_sender) and returns: content_class, typogrify, typogrify