|
"""reStructuredText parser. |
|
|
|
Contains parser for md files. |
|
|
|
""" |
|
import re |
|
from pathlib import Path |
|
from typing import Any, Dict, List, Optional, Tuple, Union |
|
|
|
from application.parser.file.base_parser import BaseParser |
|
|
|
|
|
class RstParser(BaseParser): |
|
"""reStructuredText parser. |
|
|
|
Extract text from .rst files. |
|
Returns dictionary with keys as headers and values as the text between headers. |
|
|
|
""" |
|
|
|
def __init__( |
|
self, |
|
*args: Any, |
|
remove_hyperlinks: bool = True, |
|
remove_images: bool = True, |
|
remove_table_excess: bool = True, |
|
remove_interpreters: bool = True, |
|
remove_directives: bool = True, |
|
remove_whitespaces_excess: bool = True, |
|
|
|
remove_characters_excess: bool = True, |
|
**kwargs: Any, |
|
) -> None: |
|
"""Init params.""" |
|
super().__init__(*args, **kwargs) |
|
self._remove_hyperlinks = remove_hyperlinks |
|
self._remove_images = remove_images |
|
self._remove_table_excess = remove_table_excess |
|
self._remove_interpreters = remove_interpreters |
|
self._remove_directives = remove_directives |
|
self._remove_whitespaces_excess = remove_whitespaces_excess |
|
self._remove_characters_excess = remove_characters_excess |
|
|
|
def rst_to_tups(self, rst_text: str) -> List[Tuple[Optional[str], str]]: |
|
"""Convert a reStructuredText file to a dictionary. |
|
|
|
The keys are the headers and the values are the text under each header. |
|
|
|
""" |
|
rst_tups: List[Tuple[Optional[str], str]] = [] |
|
lines = rst_text.split("\n") |
|
|
|
current_header = None |
|
current_text = "" |
|
|
|
for i, line in enumerate(lines): |
|
header_match = re.match(r"^[^\S\n]*[-=]+[^\S\n]*$", line) |
|
if header_match and i > 0 and ( |
|
len(lines[i - 1].strip()) == len(header_match.group().strip()) or lines[i - 2] == lines[i - 2]): |
|
if current_header is not None: |
|
if current_text == "" or None: |
|
continue |
|
|
|
if current_text.endswith(lines[i - 1] + "\n"): |
|
current_text = current_text[:len(current_text) - len(lines[i - 1] + "\n")] |
|
rst_tups.append((current_header, current_text)) |
|
|
|
current_header = lines[i - 1] |
|
current_text = "" |
|
else: |
|
current_text += line + "\n" |
|
|
|
rst_tups.append((current_header, current_text)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if current_header is None: |
|
rst_tups = [ |
|
(key, re.sub("\n", "", value)) for key, value in rst_tups |
|
] |
|
return rst_tups |
|
|
|
def remove_images(self, content: str) -> str: |
|
pattern = r"\.\. image:: (.*)" |
|
content = re.sub(pattern, "", content) |
|
return content |
|
|
|
def remove_hyperlinks(self, content: str) -> str: |
|
pattern = r"`(.*?) <(.*?)>`_" |
|
content = re.sub(pattern, r"\1", content) |
|
return content |
|
|
|
def remove_directives(self, content: str) -> str: |
|
"""Removes reStructuredText Directives""" |
|
pattern = r"`\.\.([^:]+)::" |
|
content = re.sub(pattern, "", content) |
|
return content |
|
|
|
def remove_interpreters(self, content: str) -> str: |
|
"""Removes reStructuredText Interpreted Text Roles""" |
|
pattern = r":(\w+):" |
|
content = re.sub(pattern, "", content) |
|
return content |
|
|
|
def remove_table_excess(self, content: str) -> str: |
|
"""Pattern to remove grid table separators""" |
|
pattern = r"^\+[-]+\+[-]+\+$" |
|
content = re.sub(pattern, "", content, flags=re.MULTILINE) |
|
return content |
|
|
|
def remove_whitespaces_excess(self, content: List[Tuple[str, Any]]) -> List[Tuple[str, Any]]: |
|
"""Pattern to match 2 or more consecutive whitespaces""" |
|
pattern = r"\s{2,}" |
|
content = [(key, re.sub(pattern, " ", value)) for key, value in content] |
|
return content |
|
|
|
def remove_characters_excess(self, content: List[Tuple[str, Any]]) -> List[Tuple[str, Any]]: |
|
"""Pattern to match 2 or more consecutive characters""" |
|
pattern = r"(\S)\1{2,}" |
|
content = [(key, re.sub(pattern, r"\1\1\1", value, flags=re.MULTILINE)) for key, value in content] |
|
return content |
|
|
|
def _init_parser(self) -> Dict: |
|
"""Initialize the parser with the config.""" |
|
return {} |
|
|
|
def parse_tups( |
|
self, filepath: Path, errors: str = "ignore" |
|
) -> List[Tuple[Optional[str], str]]: |
|
"""Parse file into tuples.""" |
|
with open(filepath, "r") as f: |
|
content = f.read() |
|
if self._remove_hyperlinks: |
|
content = self.remove_hyperlinks(content) |
|
if self._remove_images: |
|
content = self.remove_images(content) |
|
if self._remove_table_excess: |
|
content = self.remove_table_excess(content) |
|
if self._remove_directives: |
|
content = self.remove_directives(content) |
|
if self._remove_interpreters: |
|
content = self.remove_interpreters(content) |
|
rst_tups = self.rst_to_tups(content) |
|
if self._remove_whitespaces_excess: |
|
rst_tups = self.remove_whitespaces_excess(rst_tups) |
|
if self._remove_characters_excess: |
|
rst_tups = self.remove_characters_excess(rst_tups) |
|
return rst_tups |
|
|
|
def parse_file( |
|
self, filepath: Path, errors: str = "ignore" |
|
) -> Union[str, List[str]]: |
|
"""Parse file into string.""" |
|
tups = self.parse_tups(filepath, errors=errors) |
|
results = [] |
|
|
|
for header, value in tups: |
|
if header is None: |
|
results.append(value) |
|
else: |
|
results.append(f"\n\n{header}\n{value}") |
|
return results |
|
|