From 7769b12cda8218922d1d5f0069a2faca16d74413 Mon Sep 17 00:00:00 2001 From: Greenscreener Date: Fri, 21 Jul 2023 12:56:07 +0200 Subject: [PATCH] Resolve #23. --- src/formatitko/command.py | 9 +- src/formatitko/html_generator.py | 10 +- src/formatitko/images.py | 12 +-- src/formatitko/katex.py | 4 +- src/formatitko/latex_generator.py | 8 +- src/formatitko/output_generator.py | 160 +++++++++++++++-------------- 6 files changed, 103 insertions(+), 100 deletions(-) diff --git a/src/formatitko/command.py b/src/formatitko/command.py index 0fd3b4e..99ff04f 100644 --- a/src/formatitko/command.py +++ b/src/formatitko/command.py @@ -1,5 +1,4 @@ from panflute import Div, Span, Para, Element -from typing import List # Import local files from .util import nullify, import_md @@ -11,7 +10,7 @@ class Command: # This distinction is needed because while transforming the tree, inline # elements cannot be replaced with block ones class InlineCommand(Span, Command): - def replaceSelf(self, content: List[Element]) -> Span: + def replaceSelf(self, content: list[Element]) -> Span: try: return Span(*content) except TypeError: @@ -22,7 +21,7 @@ class InlineCommand(Span, Command): pass class BlockCommand(Div, Command): - def replaceSelf(self, content: List[Element]) -> Div: + def replaceSelf(self, content: list[Element]) -> Div: return Div(*content) pass @@ -58,7 +57,7 @@ def handle_command_define(e: Element, c: Context): # # These two types, appending and printing, cannot be mixed. -def executeCommand(source, element: Element, ctx: Context) -> List[Element]: +def executeCommand(source, element: Element, ctx: Context) -> list[Element]: mode = 'empty' text = "" content = [] @@ -79,7 +78,7 @@ def executeCommand(source, element: Element, ctx: Context) -> List[Element]: mode = 'elements' content.append(e) - def appendChildren(l: List[Element]): + def appendChildren(l: list[Element]): for e in l: appendChild(e) diff --git a/src/formatitko/html_generator.py b/src/formatitko/html_generator.py index 5e6af2d..18f13e4 100644 --- a/src/formatitko/html_generator.py +++ b/src/formatitko/html_generator.py @@ -2,7 +2,7 @@ from panflute import Cite, Emph, Image, LineBreak, Link, Math, Note, RawInline, from panflute import BulletList, Citation, CodeBlock, Definition, DefinitionItem, DefinitionList, Header, HorizontalRule, LineBlock, LineItem, ListItem, Null, OrderedList, Para, Plain, RawBlock, TableBody, TableFoot, TableHead from panflute import TableRow, TableCell, Caption, Doc from panflute import ListContainer, Element -from typing import Union, Dict +from typing import Union import os import io @@ -40,16 +40,16 @@ class HTMLGenerator(OutputGenerator): # text = text.replace(" ", " ") # Don't replace no-break spaces with HTML escapes, because we trust unicode? return text - def start_tag(self, tag: str, attributes: Dict[str,str]={}) -> str: + def start_tag(self, tag: str, attributes: dict[str,str]={}) -> str: words = [tag] for key, value in attributes.items(): words.append(f"{key}=\"{self.escape_special_chars(value)}\"") return "<" + " ".join(words) + ">" - def end_tag(self, tag: str, attributes: Dict[str,str]={}) -> str: + def end_tag(self, tag: str, attributes: dict[str,str]={}) -> str: return "" - def single_tag(self, tag: str, attributes: Dict[str,str]={}) -> str: + def single_tag(self, tag: str, attributes: dict[str,str]={}) -> str: return self.start_tag(tag, attributes) def tagname(self, e) -> str: @@ -82,7 +82,7 @@ class HTMLGenerator(OutputGenerator): except KeyError: return type(e).__name__.lower() - def common_attributes(self, e) -> Dict[str,str]: + def common_attributes(self, e) -> dict[str,str]: attributes = {} if hasattr(e, "identifier") and e.identifier != "": attributes["id"] = e.identifier diff --git a/src/formatitko/images.py b/src/formatitko/images.py index f084302..7623315 100644 --- a/src/formatitko/images.py +++ b/src/formatitko/images.py @@ -1,4 +1,4 @@ -from typing import List, Union, Tuple +from typing import Union import os import shutil import subprocess @@ -14,7 +14,7 @@ class ImageMagickError(Exception): pass class ImageProcessor: - def __init__(self, public_dir: str, web_path: str, cache_dir: str, *lookup_dirs: List[str]): + def __init__(self, public_dir: str, web_path: str, cache_dir: str, *lookup_dirs: list[str]): self.public_dir = public_dir self.cache_dir = cache_dir self.lookup_dirs = lookup_dirs @@ -24,7 +24,7 @@ class ImageProcessor: if not os.path.exists(self.cache_dir): os.mkdir(self.cache_dir) - def process_image(self, input_filename: str, format: str, source_dir: str, width: int=None, height:int=None, quality: int=None, dpi: int=None, fit: bool=True, deps: List[str]=[]) -> str: + def process_image(self, input_filename: str, format: str, source_dir: str, width: int=None, height:int=None, quality: int=None, dpi: int=None, fit: bool=True, deps: list[str]=[]) -> str: name = os.path.basename(input_filename) base, ext = os.path.splitext(name) ext = ext[1:] @@ -85,7 +85,7 @@ class ImageProcessor: return target_name - def is_outdated(self, target: str, deps: List[str]): + def is_outdated(self, target: str, deps: list[str]): target_timestamp = os.path.getmtime(target) for dep in deps: dep_timestamp = os.path.getmtime(dep) @@ -113,7 +113,7 @@ class ImageProcessor: raise e return target_name if relative else target_path - def get_image_size(self, input_filename: str, additional_dirs: List[str]=[]) -> Tuple[int, int]: + def get_image_size(self, input_filename: str, additional_dirs: list[str]=[]) -> tuple[int, int]: full_path = self.find_image(input_filename, additional_dirs) if full_path is None: raise FileNotFoundError(f'Image {input_filename} not found.') @@ -121,7 +121,7 @@ class ImageProcessor: return Image.open(full_path).size - def find_image(self, input_filename: str, additional_dirs: List[str]=[]) -> Union[str, None]: + def find_image(self, input_filename: str, additional_dirs: list[str]=[]) -> Union[str, None]: for dir in [*self.lookup_dirs, *additional_dirs]: if os.path.isfile(dir + "/" + input_filename): return dir + "/" + input_filename diff --git a/src/formatitko/katex.py b/src/formatitko/katex.py index 711b1d3..5ee78a7 100644 --- a/src/formatitko/katex.py +++ b/src/formatitko/katex.py @@ -3,8 +3,6 @@ import subprocess import tempfile import json import os -from typing import Dict - class KatexError(Exception): pass @@ -45,7 +43,7 @@ class KatexClient: self._client.connect(self._socket_file) - def render(self, tex: str, options: Dict={}): + def render(self, tex: str, options: dict={}): # Send formulas to translate self._client.sendall((json.dumps({"formulas":[{"tex":tex}], "options":options})+"\n").encode("utf-8")) diff --git a/src/formatitko/latex_generator.py b/src/formatitko/latex_generator.py index 202c636..92f9082 100644 --- a/src/formatitko/latex_generator.py +++ b/src/formatitko/latex_generator.py @@ -2,7 +2,7 @@ from panflute import Element, ListContainer, Inline, Block from panflute import Cite, Code, Emph, Image, LineBreak, Link, Math, Note, Quoted, RawInline, SmallCaps, SoftBreak, Space, Span, Str, Strikeout, Strong, Subscript, Superscript, Underline from panflute import BlockQuote, BulletList, Citation, CodeBlock, Definition, DefinitionItem, DefinitionList, Div, Figure, Header, HorizontalRule, LineBlock, LineItem, ListItem, MetaBlocks, MetaBool, MetaInlines, MetaList, MetaMap, MetaString, Null, OrderedList, Para, Plain, RawBlock, Table, TableBody, TableFoot, TableHead from panflute import TableRow, TableCell, Caption, Doc -from typing import Union, Dict +from typing import Union from .whitespace import NBSP from .transform import FQuoted @@ -34,13 +34,13 @@ class LaTeXGenerator(OutputGenerator): text = text.replace(" ", "~") # We use unicode no-break spaces to force nbsp in output return text - def start_tag(self, tag: str, attributes: Dict[str,str]={}) -> str: + def start_tag(self, tag: str, attributes: dict[str,str]={}) -> str: return "\\" + tag + "{" - def end_tag(self, tag: str, attributes: Dict[str,str]={}) -> str: + def end_tag(self, tag: str, attributes: dict[str,str]={}) -> str: return "}" - def single_tag(self, tag: str, attributes: Dict[str,str]={}) -> str: + def single_tag(self, tag: str, attributes: dict[str,str]={}) -> str: return "\\" + tag + "{}" diff --git a/src/formatitko/output_generator.py b/src/formatitko/output_generator.py index 80bfdb1..b1ef03f 100644 --- a/src/formatitko/output_generator.py +++ b/src/formatitko/output_generator.py @@ -2,7 +2,7 @@ from panflute import Element, ListContainer, Inline, Block from panflute import Cite, Code, Emph, Image, LineBreak, Link, Math, Note, Quoted, RawInline, SmallCaps, SoftBreak, Space, Span, Str, Strikeout, Strong, Subscript, Superscript, Underline from panflute import BlockQuote, BulletList, Citation, CodeBlock, Definition, DefinitionItem, DefinitionList, Div, Figure, Header, HorizontalRule, LineBlock, LineItem, ListItem, MetaBlocks, MetaBool, MetaInlines, MetaList, MetaMap, MetaString, Null, OrderedList, Para, Plain, RawBlock, Table, TableBody, TableFoot, TableHead from panflute import TableRow, TableCell, Caption, Doc -from typing import Union, Dict, List +from typing import Union from .whitespace import NBSP from .transform import FQuoted @@ -20,9 +20,74 @@ class OutputGenerator: self.indent_str = indent_str self.indent_level = initial_indent_level self._at_start_of_line = True - - def generate(self, e: Union[Element, ListContainer, List[Union[Element, ListContainer]]]): - if isinstance(e, List): + + self.TYPE_DICT_MISC = { + TableRow: self.generate_TableRow, + TableCell: self.generate_TableCell, + Caption: self.generate_Caption, + Doc: self.generate_Doc, + LineItem: self.generate_LineItem, + ListItem: self.generate_ListItem + } + + self.TYPE_DICT_BLOCK = { + BlockQuote: self.generate_BlockQuote, + BulletList: self.generate_BulletList, + Citation: self.generate_Citation, + CodeBlock: self.generate_CodeBlock, + Definition: self.generate_Definition, + DefinitionItem: self.generate_DefinitionItem, + DefinitionList: self.generate_DefinitionList, + Div: self.generate_Div, + Figure: self.generate_Figure, + Header: self.generate_Header, + HorizontalRule: self.generate_HorizontalRule, + LineBlock: self.generate_LineBlock, + MetaBlocks: self.generate_MetaBlocks, + MetaBool: self.generate_MetaBool, + MetaInlines: self.generate_MetaInlines, + MetaList: self.generate_MetaList, + MetaMap: self.generate_MetaMap, + MetaString: self.generate_MetaString, + Null: self.generate_Null, + OrderedList: self.generate_OrderedList, + Para: self.generate_Para, + Plain: self.generate_Plain, + RawBlock: self.generate_RawBlock, + Table: self.generate_Table, + TableBody: self.generate_TableBody, + TableFoot: self.generate_TableFoot, + TableHead: self.generate_TableHead, + Group: self.generate_Group + } + + self.TYPE_DICT_INLINE = { + Cite: self.generate_Cite, + Code: self.generate_Code, + Emph: self.generate_Emph, + Image: self.generate_Image, + LineBreak: self.generate_LineBreak, + Link: self.generate_Link, + Math: self.generate_Math, + Note: self.generate_Note, + Quoted: self.generate_Quoted, + RawInline: self.generate_RawInline, + SmallCaps: self.generate_SmallCaps, + SoftBreak: self.generate_SoftBreak, + Space: self.generate_Space, + Span: self.generate_Span, + Str: self.generate_Str, + Strikeout: self.generate_Strikeout, + Strong: self.generate_Strong, + Subscript: self.generate_Subscript, + Superscript: self.generate_Superscript, + Underline: self.generate_Underline, + NBSP: self.generate_NBSP, + FQuoted: self.generate_FQuoted + } + + def generate(self, e: Union[Element, ListContainer, list[Union[Element, ListContainer]]]): + if isinstance(e, list): for el in e: self.generate(el) elif isinstance(e, ListContainer): @@ -33,14 +98,7 @@ class OutputGenerator: self.generate_Block(e) else: try: - { - TableRow: self.generate_TableRow, - TableCell: self.generate_TableCell, - Caption: self.generate_Caption, - Doc: self.generate_Doc, - LineItem: self.generate_LineItem, - ListItem: self.generate_ListItem - }[type(e)](e) + self.TYPE_DICT_MISC[type(e)](e) except KeyError: raise UnknownElementError(type(e)) @@ -80,22 +138,22 @@ class OutputGenerator: self.output_file.write("\n") self._at_start_of_line = True - def start_tag(self, tag: str, attributes: Dict[str,str]={}) -> str: + def start_tag(self, tag: str, attributes: dict[str,str]={}) -> str: return tag - def end_tag(self, tag: str, attributes: Dict[str,str]={}) -> str: + def end_tag(self, tag: str, attributes: dict[str,str]={}) -> str: return "/" + tag - def single_tag(self, tag: str, attributes: Dict[str,str]={}) -> str: + def single_tag(self, tag: str, attributes: dict[str,str]={}) -> str: return "/" + tag + "/" def tagname(self, e) -> str: return type(e).__name__ - def common_attributes(self, e: Element) -> Dict[str,str]: + def common_attributes(self, e: Element) -> dict[str,str]: return {} - def generate_simple_tag(self, e: Union[Element, None]=None, tag: str="", attributes: Union[Dict[str,str],None]=None, content: Union[ListContainer, Element, List[Union[Element, ListContainer]], str, None]=None, inline: Union[bool, None]=None): + def generate_simple_tag(self, e: Union[Element, None]=None, tag: str="", attributes: Union[dict[str,str],None]=None, content: Union[ListContainer, Element, list[Union[Element, ListContainer]], str, None]=None, inline: Union[bool, None]=None): if not tag and e: tag = self.tagname(e) if attributes is None and e: @@ -124,29 +182,29 @@ class OutputGenerator: else: self.generate_simple_block_tag(tag, content, attributes) - def generate_simple_inline_tag(self, tag: str, content: Union[ListContainer, Element, List[Union[Element, ListContainer]]], attributes: Dict[str,str]={}): + def generate_simple_inline_tag(self, tag: str, content: Union[ListContainer, Element, list[Union[Element, ListContainer]]], attributes: dict[str,str]={}): self.write(self.start_tag(tag, attributes)) self.generate(content) self.write(self.end_tag(tag)) - def generate_simple_block_tag(self, tag: str, content: Union[ListContainer, Element, List[Union[Element, ListContainer]]], attributes: Dict[str,str]={}): + def generate_simple_block_tag(self, tag: str, content: Union[ListContainer, Element, list[Union[Element, ListContainer]]], attributes: dict[str,str]={}): self.writeln(self.start_tag(tag, attributes)) self.indent_more() self.generate(content) self.indent_less() self.writeln(self.end_tag(tag)) - def generate_raw_inline_tag(self, tag: str, text: str, attributes: Dict[str,str]={}): + def generate_raw_inline_tag(self, tag: str, text: str, attributes: dict[str,str]={}): self.write(self.start_tag(tag, attributes)) self.write(text) self.write(self.end_tag(tag)) - def generate_raw_block_tag(self, tag: str, text: str, attributes: Dict[str,str]={}): + def generate_raw_block_tag(self, tag: str, text: str, attributes: dict[str,str]={}): self.writeln(self.start_tag(tag, attributes)) self.writeraw(text) self.writeln(self.end_tag(tag)) - def generate_empty_block_tag(self, tag: str, attributes: Dict[str,str]={}): + def generate_empty_block_tag(self, tag: str, attributes: dict[str,str]={}): self.writeln(self.single_tag(tag, attributes)) def generate_ListContainer(self, e: ListContainer): @@ -154,30 +212,7 @@ class OutputGenerator: self.generate(child) def generate_Inline(self, e: Inline): - { - Cite: self.generate_Cite, - Code: self.generate_Code, - Emph: self.generate_Emph, - Image: self.generate_Image, - LineBreak: self.generate_LineBreak, - Link: self.generate_Link, - Math: self.generate_Math, - Note: self.generate_Note, - Quoted: self.generate_Quoted, - RawInline: self.generate_RawInline, - SmallCaps: self.generate_SmallCaps, - SoftBreak: self.generate_SoftBreak, - Space: self.generate_Space, - Span: self.generate_Span, - Str: self.generate_Str, - Strikeout: self.generate_Strikeout, - Strong: self.generate_Strong, - Subscript: self.generate_Subscript, - Superscript: self.generate_Superscript, - Underline: self.generate_Underline, - NBSP: self.generate_NBSP, - FQuoted: self.generate_FQuoted - }[type(e)](e) + self.TYPE_DICT_INLINE[type(e)](e) def generate_Str(self, e: Str): self.write(self.escape_special_chars(e.text)) @@ -278,36 +313,7 @@ class OutputGenerator: def generate_Block(self, e: Block): - { - BlockQuote: self.generate_BlockQuote, - BulletList: self.generate_BulletList, - Citation: self.generate_Citation, - CodeBlock: self.generate_CodeBlock, - Definition: self.generate_Definition, - DefinitionItem: self.generate_DefinitionItem, - DefinitionList: self.generate_DefinitionList, - Div: self.generate_Div, - Figure: self.generate_Figure, - Header: self.generate_Header, - HorizontalRule: self.generate_HorizontalRule, - LineBlock: self.generate_LineBlock, - MetaBlocks: self.generate_MetaBlocks, - MetaBool: self.generate_MetaBool, - MetaInlines: self.generate_MetaInlines, - MetaList: self.generate_MetaList, - MetaMap: self.generate_MetaMap, - MetaString: self.generate_MetaString, - Null: self.generate_Null, - OrderedList: self.generate_OrderedList, - Para: self.generate_Para, - Plain: self.generate_Plain, - RawBlock: self.generate_RawBlock, - Table: self.generate_Table, - TableBody: self.generate_TableBody, - TableFoot: self.generate_TableFoot, - TableHead: self.generate_TableHead, - Group: self.generate_Group - }[type(e)](e) + self.TYPE_DICT_BLOCK[type(e)](e) # Block elements @@ -420,7 +426,7 @@ class OutputGenerator: self.generate_simple_tag(e) # Maybe move this to ImageProcessor? - def get_image_processor_args(self, attributes:Dict[str,str]) -> Dict: + def get_image_processor_args(self, attributes:dict[str,str]) -> dict: # Attributes → image processor args additional_args = {} if "file-width" in attributes: