doc: Introduce reqparse, the plain text requirement parser
Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
This commit is contained in:
parent
ff00dab04c
commit
a901475f1b
153
doc/reqparse
Executable file
153
doc/reqparse
Executable file
@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright(c) 2021 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
|
||||
import argparse
|
||||
import enum
|
||||
import glob
|
||||
import io
|
||||
import markdown
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
import yaml.scanner
|
||||
|
||||
|
||||
def error(filename, line, column, string):
|
||||
string = "\n ".join(string.split('\n'))
|
||||
print(f"[ERROR] {filename}: line {line}, column {column}: {string}", file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
|
||||
class Entity:
|
||||
def __init__(self):
|
||||
self.header_text = ""
|
||||
self.header = None
|
||||
self.text = ""
|
||||
|
||||
|
||||
class State(enum.Enum):
|
||||
BEGIN = enum.auto()
|
||||
GROUP_HEADER = enum.auto()
|
||||
GROUP_TEXT = enum.auto()
|
||||
REQ_HEADER_PRE = enum.auto()
|
||||
REQ_HEADER = enum.auto()
|
||||
REQ_TEXT = enum.auto()
|
||||
|
||||
|
||||
def parse_header(entity, filename, line):
|
||||
try:
|
||||
entity.header = yaml.safe_load(entity.header_text)
|
||||
except yaml.scanner.ScannerError as e:
|
||||
error(filename, line+e.problem_mark.line+1,
|
||||
e.problem_mark.column+1, f"{e.context}\n{e.problem}")
|
||||
|
||||
|
||||
def parse_file(filename):
|
||||
state = State.BEGIN
|
||||
group = None
|
||||
reqs = []
|
||||
current_entity = None
|
||||
header_line = 0
|
||||
with open(filename, "r") as f:
|
||||
for i, l in enumerate(f.readlines(), start=1):
|
||||
if l.strip() == "---":
|
||||
if state == State.BEGIN:
|
||||
current_entity = Entity()
|
||||
state = State.GROUP_HEADER
|
||||
header_line = i
|
||||
elif state == State.GROUP_HEADER:
|
||||
parse_header(current_entity, filename, header_line)
|
||||
state = State.GROUP_TEXT
|
||||
elif state == State.GROUP_TEXT:
|
||||
error(filename, i, 1, "unexpected \"---\",\n"
|
||||
"expected markdown or req header marker")
|
||||
elif state == State.REQ_HEADER_PRE:
|
||||
error(filename, i, 1, "unexpected \"---\",\n"
|
||||
"expected another line of req header marker")
|
||||
elif state == State.REQ_HEADER:
|
||||
parse_header(current_entity, filename, header_line)
|
||||
state = State.REQ_TEXT
|
||||
elif state == State.REQ_TEXT:
|
||||
error(filename, i, 1, "unexpected \"---\",\n"
|
||||
"expected markdown or next req header marker")
|
||||
else:
|
||||
error(filename, i, 1, "something went terribly wrong")
|
||||
elif l.strip() == "-"*80:
|
||||
if state == State.BEGIN:
|
||||
error(filename, i, 1, "unexpected req header marker,\n"
|
||||
"expected group header marker")
|
||||
elif state == State.GROUP_HEADER:
|
||||
error(filename, i, 1, "unexpected req header marker,\n"
|
||||
"expected yaml or group header end marker")
|
||||
elif state == State.GROUP_TEXT:
|
||||
group = current_entity
|
||||
current_entity = None
|
||||
state = State.REQ_HEADER_PRE
|
||||
elif state == State.REQ_HEADER_PRE:
|
||||
current_entity = Entity()
|
||||
state = State.REQ_HEADER
|
||||
header_line = i
|
||||
elif state == State.REQ_HEADER:
|
||||
error(filename, i, 1, "unexpected req header marker,\n"
|
||||
"expected markdown or req header end marker")
|
||||
elif state == State.REQ_TEXT:
|
||||
reqs.append(current_entity)
|
||||
current_entity = None
|
||||
state = State.REQ_HEADER_PRE
|
||||
else:
|
||||
error(filename, i, 1, "something went terribly wrong")
|
||||
else:
|
||||
if state == State.BEGIN:
|
||||
error(filename, i, 1, "unexpected character, expected group header")
|
||||
elif state == State.GROUP_HEADER:
|
||||
current_entity.header_text += l
|
||||
elif state == State.GROUP_TEXT:
|
||||
current_entity.text += l
|
||||
elif state == State.REQ_HEADER_PRE:
|
||||
error(filename, i, 1, "expected another line of req header marker")
|
||||
elif state == State.REQ_HEADER:
|
||||
current_entity.header_text += l
|
||||
elif state == State.REQ_TEXT:
|
||||
current_entity.text += l
|
||||
else:
|
||||
error(filename, i, 1, "something went terribly wrong")
|
||||
if state == State.REQ_TEXT:
|
||||
reqs.append(current_entity)
|
||||
return (group, reqs)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(prog="reqparse")
|
||||
parser.add_argument(
|
||||
"-o", "--output", action="store",
|
||||
help="Output file"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f", "--format", action="store",
|
||||
choices=["html", "markdown"], default="markdown",
|
||||
help="Output format {html|markdown}",
|
||||
)
|
||||
|
||||
args = parser.parse_args(sys.argv[1:])
|
||||
|
||||
buf = ""
|
||||
with io.StringIO() as output:
|
||||
for filename in glob.glob("requirements/*"):
|
||||
group, reqs = parse_file(filename)
|
||||
output.write(f"# {group.header['group']}\n")
|
||||
output.write(group.text)
|
||||
for r in reqs:
|
||||
output.write(f"## {r.header['title']}\n")
|
||||
output.write(r.text)
|
||||
buf = output.getvalue()
|
||||
|
||||
if args.format == "html":
|
||||
buf = markdown.markdown(buf, extensions=['sane_lists'])
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w+") as outfile:
|
||||
outfile.write(buf)
|
||||
else:
|
||||
print(buf)
|
42
doc/requirement_example
Normal file
42
doc/requirement_example
Normal file
@ -0,0 +1,42 @@
|
||||
---
|
||||
group: Requirement group title
|
||||
---
|
||||
|
||||
Requirement group description.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
title: Requirement about requirements
|
||||
id: req_syntax
|
||||
---
|
||||
|
||||
It shall be possible specify requirement header and body.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
title: Markdown in requirements body
|
||||
id: req_body_markdown
|
||||
---
|
||||
|
||||
It shall be possible use markdown language in requirement body.
|
||||
The feature list includes:
|
||||
|
||||
- **bold**, *italics* text
|
||||
- ordered and unordered lists
|
||||
- tables
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
title: YAML in requirements header
|
||||
id: req_header_yaml
|
||||
---
|
||||
|
||||
It shall be possible to put any YAML content in the requirement header
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
title: Multiple requirements
|
||||
id: multiple_requirements
|
||||
---
|
||||
|
||||
It shall be possible to place multiple requirements in one file.
|
Loading…
Reference in New Issue
Block a user