From db5a50c51bf6405e935eb47287bca9dd64a4b556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Boisg=C3=A9rault?= Date: Wed, 21 Aug 2024 18:26:29 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactoring=20and=20new=20?= =?UTF-8?q?doctests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pandoc/types/__init__.py | 10 +-- src/pandoc/types/parser.py | 151 +++++++++++++++++++++++++++++++---- test.py | 38 ++++++--- 3 files changed, 164 insertions(+), 35 deletions(-) diff --git a/src/pandoc/types/__init__.py b/src/pandoc/types/__init__.py index 76435b8..cab76c2 100644 --- a/src/pandoc/types/__init__.py +++ b/src/pandoc/types/__init__.py @@ -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: @@ -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) \ No newline at end of file diff --git a/src/pandoc/types/parser.py b/src/pandoc/types/parser.py index 24e76c0..783970e 100644 --- a/src/pandoc/types/parser.py +++ b/src/pandoc/types/parser.py @@ -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', ... [ @@ -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', ... [ @@ -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', @@ -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__]) \ No newline at end of file + return [parse(type_decl) for type_decl in split(src)] \ No newline at end of file diff --git a/test.py b/test.py index 8cf9daf..c6f5358 100755 --- a/test.py +++ b/test.py @@ -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 @@ -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 * "-")