Skip to content

Commit

Permalink
♻️ Refactoring and new doctests
Browse files Browse the repository at this point in the history
  • Loading branch information
boisgera committed Aug 21, 2024
1 parent c21401a commit db5a50c
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 35 deletions.
10 changes: 2 additions & 8 deletions src/pandoc/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ def make_types(
if not isinstance(defs_src, str): # resource loaded as bytes in Python 3
defs_src = defs_src.decode("utf-8")

defs = parser.parse(defs_src)
defs = parser.parse_defs(defs_src)

# Create the types
for decl in defs:
Expand Down Expand Up @@ -564,10 +564,4 @@ def set_data_repr(
# Create Types
# ------------------------------------------------------------------------------
if pandoc.configure(read=True) is None:
pandoc.configure(auto=True)


# Tempory; integrate into the main test suite
def test():
import doctest
doctest.testmod(verbose=True)
pandoc.configure(auto=True)
151 changes: 136 additions & 15 deletions src/pandoc/types/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,139 @@ def keep(line):
type_decls[-1] = type_decls[-1] + "\n" + line
return type_decls

def parse(text: str) -> Decl:
'''
def parse(src: str) -> list[Decl]:
TODO: !, (), newtype,
Type Synonyms, List, Tuple, Map & Maybe
---------------------------------------
>>> parse("type Text = String")
['type', ['Text', 'String']]
>>> parse("type Text = (String)")
['type', ['Text', 'String']]
>>> parse("type Text = ((String))")
['type', ['Text', 'String']]
>>> parse("type Numbers = [Int]")
['type', ['Numbers', ['list', ['Int']]]]
>>> parse("type Sequences = [[Int]]")
['type', ['Sequences', ['list', [['list', ['Int']]]]]]
>>> parse("type Pair = (String, Int)")
['type', ['Pair', ['tuple', ['String', 'Int']]]]
>>> parse("type Count = Map String Int")
['type', ['Count', ['map', ['String', 'Int']]]]
>>> parse("type OptionalText = Maybe String")
['type', ['OptionalText', ['maybe', ['String']]]]
These patterns may be nested; a real-life Pandoc example would be:
>>> parse("type Attr = (String, [String], [(String, String)])") == \\
... ['type',
... ['Attr',
... ['tuple',
... [
... 'String',
... ['list', ['String']],
... ['list', [['tuple', ['String', 'String']]]]
... ]
... ]
... ]
... ]
True
Data Types
----------
>>> parse("""
... data Citation
... = Citation {citationId :: Text,
... citationPrefix :: [Inline],
... citationSuffix :: [Inline],
... citationMode :: CitationMode,
... citationNoteNum :: Int,
... citationHash :: Int}
... """) == \\
... ['data',
... ['Citation',
... [
... ['Citation',
... ['map',
... [
... ['citationId', 'Text'],
... ['citationPrefix', ['list', ['Inline']]],
... ['citationSuffix', ['list', ['Inline']]],
... ['citationMode', 'CitationMode'],
... ['citationNoteNum', 'Int'],
... ['citationHash', 'Int']
... ]
... ]
... ]
... ]
... ]
... ]
True
>>> parse("newtype Meta = Meta {unMeta :: Map Text MetaValue}") == \\
... ['newtype',
... ['Meta',
... [
... ['Meta',
... ['map',
... [
... ['unMeta', ['map', ['Text', 'MetaValue']]]
... ]
... ]
... ]
... ]
... ]
... ]
True
>>> parse("newtype Format = Format Text") == \\
... ['newtype',
... ['Format',
... [
... ['Format',
... ['list',
... ['Text']
... ]
... ]
... ]
... ]
... ]
True
>>> parse("data Caption = Caption (Maybe ShortCaption) [Block]") == \\
... ['data',
... ['Caption',
... [
... ['Caption',
... ['list',
... [
... ['maybe', ['ShortCaption']],
... ['list', ['Block']]
... ]
... ]
... ]
... ]
... ]
... ]
True
'''
return parser.parse(text)

def parse_defs(src: str) -> list[Decl]:
"""
>>> parse("data Color = Red | Green | Blue") == [
>>> parse_defs("data Color = Red | Green | Blue") == [
... ['data',
... ['Color',
... [
Expand All @@ -238,7 +367,7 @@ def parse(src: str) -> list[Decl]:
... ]
True
>>> parse("data Alignment = AlignLeft | AlignRight | AlignCenter | AlignDefault") == [
>>> parse_defs("data Alignment = AlignLeft | AlignRight | AlignCenter | AlignDefault") == [
... ['data',
... ['Alignment',
... [
Expand All @@ -251,21 +380,21 @@ def parse(src: str) -> list[Decl]:
... ]
... ]
True
>>> parse("data Pandoc = Pandoc Meta [Block]") # doctest: +NORMALIZE_WHITESPACE
>>> parse_defs("data Pandoc = Pandoc Meta [Block]") # doctest: +NORMALIZE_WHITESPACE
[['data',
['Pandoc',
[['Pandoc',
['list',
['Meta',
['list', ['Block']]]]]]]]]
>>> parse("data Meta = Meta {docTitle :: [Inline], docAuthors :: [[Inline]], docDate :: [Inline]}") # doctest: +NORMALIZE_WHITESPACE
>>> parse_defs("data Meta = Meta {docTitle :: [Inline], docAuthors :: [[Inline]], docDate :: [Inline]}") # doctest: +NORMALIZE_WHITESPACE
[['data',
['Meta',
[['Meta', ['map',
[['docTitle', ['list', ['Inline']]],
['docAuthors', ['list', [['list', ['Inline']]]]],
['docDate', ['list', ['Inline']]]]]]]]]]
>>> parse("type Attr = (String, [String], [(String, String)])") # doctest: +NORMALIZE_WHITESPACE
>>> parse_defs("type Attr = (String, [String], [(String, String)])") # doctest: +NORMALIZE_WHITESPACE
[['type',
['Attr',
['tuple',
Expand All @@ -274,12 +403,4 @@ def parse(src: str) -> list[Decl]:
['list', [['tuple', ['String', 'String']]]]]]]]]
>>>
"""
return [parser.parse(type_decl) for type_decl in split(src)]


# Tempory; integrate into the main test suite
# Bug: doctests not found
def test():
import doctest
import sys
doctest.testmod(sys.modules[__name__])
return [parse(type_decl) for type_decl in split(src)]
38 changes: 26 additions & 12 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
# Python Standard Library
import codeop
import doctest
import glob
import importlib
import os
import shutil
import platform
import re
import sys
import tempfile
from typing import Any

# Third-Party Libraries
import strictyaml
Expand Down Expand Up @@ -148,29 +149,42 @@ def tweak(src):
with open(filename, "w", encoding="utf-8") as file:
file.write(src)

# Test Modules
# ------------------------------------------------------------------------------

test_modules = ["pandoc.types", "pandoc.types.parser"]


# Run the Tests
# ------------------------------------------------------------------------------
verbose = "-v" in sys.argv or "--verbose" in sys.argv

options : dict[str, Any]

options = {"module_relative": False, "verbose": verbose}
# Relax the tests to deal with test files that have a '\n' line break
# (Linux flavor) which does not match the pandoc line break on Windows
# (Windows flavor : '\r\n').
# The proper way to deal with this would be to convert the test files
# beforehand on Windows.
if platform.system() == "Windows":
options["optionflags"] = doctest.NORMALIZE_WHITESPACE

fails = 0
tests = 0
for filename in test_files:
options = {"module_relative": False, "verbose": verbose}

# Relax the tests to deal with test files that have a '\n' line break
# (Linux flavor) which does not match the pandoc line break on Windows
# (Windows flavor : '\r\n').
# The proper way to deal with this would be to convert the test files
# beforehand on Windows.
if platform.system() == "Windows":
options["optionflags"] = doctest.NORMALIZE_WHITESPACE

_fails, _tests = doctest.testfile(filename, **options)
fails += _fails
tests += _tests

os.chdir(cwd)

options = {"verbose": verbose}
for module in test_modules:
m = importlib.import_module(module)
_fails, _tests = doctest.testmod(m, **options)
fails += _fails
tests += _tests

if fails > 0 or verbose: # pragma: no cover
print()
print(60 * "-")
Expand Down

0 comments on commit db5a50c

Please sign in to comment.