class_docstr: Stream a collection of English-language literary works from text files on disk,
  either as texts or text + metadata pairs.
  
  Download the data (one time only!), saving and extracting its contents to disk::
  
      >>> import textacy.datasets
      >>> ds = textacy.datasets.OxfordTextArchive()
      >>> ds.download()
      >>> ds.info
      {'name': 'oxford_text_archive',
       'site_url': 'https://ota.ox.ac.uk/',
       'description': 'Collection of ~2.7k Creative Commons texts from the Oxford Text Archive, containing primarily English-language 16th-20th century literature and history.'}
  
  Iterate over literary works as texts or records with both text and metadata::
  
      >>> for text in ds.texts(limit=3):
      ...     print(text[:200])
      >>> for text, meta in ds.records(limit=3):
      ...     print("\n{}, {}".format(meta["title"], meta["year"]))
      ...     print(text[:300])
  
  Filter literary works by a variety of metadata fields and text length::
  
      >>> for text, meta in ds.records(author="Shakespeare, William", limit=1):
      ...     print("{}\n{}".format(meta["title"], text[:500]))
      >>> for text, meta in ds.records(date_range=("1900-01-01", "1990-01-01"), limit=5):
      ...     print(meta["year"], meta["author"])
      >>> for text in ds.texts(min_len=4000000):
      ...     print(len(text))
  
  Stream literary works into a :class:`textacy.Corpus <textacy.corpus.Corpus>`::
  
      >>> textacy.Corpus("en", data=ds.records(limit=5))
      Corpus(5 docs; 182289 tokens)
  
  Args:
      data_dir (str or :class:`pathlib.Path`): Path to directory on disk
          under which dataset is stored, i.e. ``/path/to/data_dir/oxford_text_archive``.
  
  Attributes:
      full_date_range: First and last dates for which works are available,
          each as an ISO-formatted string (YYYY-MM-DD).
      authors (set[str]): Full names of all distinct authors included in this
          dataset, e.g. "Shakespeare, William".
docstr: Read in ``metadata.tsv`` file from :attr:`OxfordTextArchive._metadata_filepath``
  zip archive; convert into a dictionary keyed by record ID; clean up some
  of the fields, and remove a couple fields that are identical throughout.
docstr_tok: ["Read","in","``","metadata.tsv","``","file","from",":","attr",":","`","OxfordTextArchive._metadata_filepath","``","zip","archive",";","convert","into","a","dictionary","keyed","by","record","ID",";","clean","up","some","of","the","fields",",","and","remove","a","couple","fields","that","are","identical","throughout","."]
code: def _load_and_parse_metadata(self) -> dict[str, dict[str, Any]]:
      """
      Read in ``metadata.tsv`` file from :attr:`OxfordTextArchive._metadata_filepath``
      zip archive; convert into a dictionary keyed by record ID; clean up some
      of the fields, and remove a couple fields that are identical throughout.
      """
      if not self._metadata_filepath.is_file():
          raise OSError(
              f"metadata file {self._metadata_filepath} not found;\n"
              "has the dataset been downloaded yet?"
          )
  
      re_extract_year = re.compile(r"(\d{4})")
      re_extract_authors = re.compile(
          r"(\D+)"
          r"(?:, "
          r"(?:[bdf]l?\. )?(?:ca. )?\d{4}(?:\?| or \d{1,2})?(?:-(?:[bdf]l?\. )?(?:ca. )?\d{4}(?:\?| or \d{1,2})?)?|"
          r"(?:\d{2}th(?:/\d{2}th)? cent\.)"
          r"\.?)"
      )
      re_clean_authors = re.compile(r"^[,;. ]+|[,.]+\s*?$")
      metadata = {}
      with self._metadata_filepath.open(mode="rb") as f:
          subf = io.StringIO(f.read().decode("utf-8"))
          for row in csv.DictReader(subf, delimiter="\t"):
              # only include English-language works (99.9% of all works)
              if not row["Language"].startswith("English"):
                  continue
              # clean up years
              year_match = re_extract_year.search(row["Year"])
              if year_match:
                  row["Year"] = year_match.group()
              else:
                  row["Year"] = None
              # extract and clean up authors
              authors = re_extract_authors.findall(row["Author"]) or [
                  row["Author"]
              ]
              row["Author"] = tuple(
                  re_clean_authors.sub("", author) for author in authors
              )
              row["Title"] = row["Title"].strip()
              # get rid of uniform "Language" and "License" fields
              del row["Language"]
              del row["License"]
              metadata[row["ID"]] = {
                  key.lower(): val for key, val in row.items()
              }
      # set authors attribute for user convenience / to validate author filtering
      self.authors = {
          author
          for value in metadata.values()
          for author in value["author"]
          if value.get("author")
      }
      return metadata
  
imports: from __future__ import annotations
  import csv
  import io
  import itertools
  import logging
  import os
  import re
  from typing import Any, ClassVar, Iterable, Optional
  from ..None import constants
  from ..None import io
  from ..None import types, utils
  from .base import Dataset
label_desc: Use image node_id 4 for calling the OxfordTextArchive obj's underlying member method code with example usage: obj._load_and_parse_metadata() and returns: metadata