comparison env/lib/python3.7/site-packages/gxformat2/markdown_parse.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
1 """Utilities for parsing "Galaxy Flavored Markdown".
2
3 See markdown_util.py for loading objects and interacting with the rest of Galaxy.
4 This file is meant to be relatively self contained and just used to "parse" and validate
5 Galaxy Markdown. Keeping things isolated to allow re-use of these utilities in other
6 projects (e.g. gxformat2).
7 """
8 import re
9
10 BLOCK_FENCE_START = re.compile(r'```.*')
11 BLOCK_FENCE_END = re.compile(r'```[\s]*')
12 GALAXY_FLAVORED_MARKDOWN_CONTAINER_LINE_PATTERN = re.compile(
13 r"```\s*galaxy\s*"
14 )
15 VALID_CONTAINER_END_PATTERN = re.compile(r"^```\s*$")
16 GALAXY_FLAVORED_MARKDOWN_CONTAINERS = [
17 "history_dataset_display",
18 "history_dataset_collection_display",
19 "history_dataset_as_image",
20 "history_dataset_peek",
21 "history_dataset_info",
22 "workflow_display",
23 "job_metrics",
24 "job_parameters",
25 "tool_stderr",
26 "tool_stdout",
27 ]
28 INVOCATION_SECTIONS = [
29 "invocation_inputs",
30 "invocation_outputs",
31 ]
32 ALL_CONTAINER_TYPES = GALAXY_FLAVORED_MARKDOWN_CONTAINERS + INVOCATION_SECTIONS
33 GALAXY_FLAVORED_MARKDOWN_CONTAINER_REGEX = "(%s)" % "|".join(ALL_CONTAINER_TYPES)
34
35 ARG_VAL_REGEX = r'''[\w_\-]+|\"[^\"]+\"|\'[^\']+\''''
36 FUNCTION_ARG = r'\s*\w+\s*=\s*(?:%s)\s*' % ARG_VAL_REGEX
37 FUNCTION_CALL_LINE_TEMPLATE = r'\s*%s\s*\((?:' + FUNCTION_ARG + r')?\)\s*'
38 GALAXY_MARKDOWN_FUNCTION_CALL_LINE = re.compile(FUNCTION_CALL_LINE_TEMPLATE % GALAXY_FLAVORED_MARKDOWN_CONTAINER_REGEX)
39 WHITE_SPACE_ONLY_PATTERN = re.compile(r"^[\s]+$")
40
41
42 def validate_galaxy_markdown(galaxy_markdown, internal=True):
43 """Validate the supplied markdown and throw an ValueError with reason if invalid."""
44 expecting_container_close_for = None
45 last_line_no = 0
46 function_calls = 0
47 for (line, fenced, open_fence, line_no) in _split_markdown_lines(galaxy_markdown):
48 last_line_no = line_no
49
50 def invalid_line(template, **kwd):
51 if "line" in kwd:
52 kwd["line"] = line.rstrip("\r\n")
53 raise ValueError("Invalid line %d: %s" % (line_no + 1, template.format(**kwd)))
54
55 expecting_container_close = expecting_container_close_for is not None
56 if not fenced and expecting_container_close:
57 invalid_line("[{line}] is not expected close line for [{expected_for}]", line=line, expected_for=expecting_container_close_for)
58 continue
59 elif not fenced:
60 continue
61 elif fenced and expecting_container_close and BLOCK_FENCE_END.match(line):
62 # reset
63 expecting_container_close_for = None
64 function_calls = 0
65 elif open_fence and GALAXY_FLAVORED_MARKDOWN_CONTAINER_LINE_PATTERN.match(line):
66 if expecting_container_close:
67 if not VALID_CONTAINER_END_PATTERN.match(line):
68 invalid_line("Invalid command close line [{line}] for [{expected_for}]", line=line, expected_for=expecting_container_close_for)
69 # else closing container and we're done
70 expecting_container_close_for = None
71 function_calls = 0
72 continue
73
74 expecting_container_close_for = line
75 continue
76 elif fenced and line and expecting_container_close_for:
77 if GALAXY_MARKDOWN_FUNCTION_CALL_LINE.match(line):
78 function_calls += 1
79 if function_calls > 1:
80 invalid_line("Only one Galaxy directive is allowed per fenced Galaxy block (```galaxy)")
81 continue
82 else:
83 invalid_line("Invalid embedded Galaxy markup line [{line}]", line=line)
84
85 # Markdown unrelated to Galaxy object containers.
86 continue
87
88 if expecting_container_close_for:
89 template = "Invalid line %d: %s"
90 msg = template % (last_line_no, "close of block for [{expected_for}] expected".format(expected_for=expecting_container_close_for))
91 raise ValueError(msg)
92
93
94 def _split_markdown_lines(markdown):
95 """Yield lines of a markdown document line-by-line keeping track of fencing.
96
97 'Fenced' lines are code-like block (e.g. between ```) that shouldn't contain
98 Markdown markup.
99 """
100 block_fenced = False
101 indent_fenced = False
102 for line_number, line in enumerate(markdown.splitlines(True)):
103 open_fence_this_iteration = False
104 indent_fenced = line.startswith(" ") or (indent_fenced and WHITE_SPACE_ONLY_PATTERN.match(line))
105 if not block_fenced:
106 if BLOCK_FENCE_START.match(line):
107 open_fence_this_iteration = True
108 block_fenced = True
109 yield (line, block_fenced or indent_fenced, open_fence_this_iteration, line_number)
110 if not open_fence_this_iteration and BLOCK_FENCE_END.match(line):
111 block_fenced = False
112
113
114 __all__ = (
115 'validate_galaxy_markdown',
116 'GALAXY_MARKDOWN_FUNCTION_CALL_LINE',
117 )