lbrycrd/contrib/devtools/generate_json_api_v1.py
lbrynaut 9295adeb38 Rebase lbry on to Bitcoin 0.17.
This contains significant rebase / merge / testing work by Naut
<lbrynaut@protonmail.com>, Anthony Fieroni <bvbfan@abv.bg> and Brannon
King <countprimes@gmail.com>.
2020-03-26 15:39:51 +02:00

129 lines
3.9 KiB
Python
Executable file

#!/usr/bin/env python3
import re
import subprocess as sp
import sys
import json
re_full = re.compile(r'(?P<name>^.*?$)(?P<desc>.*?)(^Argument.*?$(?P<args>.*?))?(^Result[^\n]*?:\s*$(?P<resl>.*?))?(^Exampl.*?$(?P<exmp>.*))?', re.DOTALL | re.MULTILINE)
re_argline = re.compile(r'^("?)(?P<name>.*?)\1\s+\((?P<type>.*?)\)\s*(?P<desc>.*)$', re.DOTALL)
def get_type(arg_type, full_line):
if arg_type is None:
return 'string'
arg_type = arg_type.lower()
if 'numeric' in arg_type:
return 'number'
if 'bool' in arg_type:
return 'boolean'
if 'string' in arg_type:
return 'string'
if 'object' in arg_type:
return 'object'
raise Exception('Not implemented: ' + arg_type)
def parse_params(args):
arguments = []
if args:
for line in re.split('\s*\d+\.\s+', args, re.DOTALL):
if not line or not line.strip() or line.strip().startswith('None'):
continue
arg_parsed = re_argline.fullmatch(line)
if arg_parsed is None:
raise Exception("Unparsable argument: " + line)
arg_name, arg_type, arg_desc = arg_parsed.group('name', 'type', 'desc')
if not arg_type:
raise Exception('Not implemented: ' + arg_type)
arg_required = 'required' in arg_type or 'optional' not in arg_type
arg_refined_type = get_type(arg_type, line)
arg_desc = re.sub('\s+', ' ', arg_desc.strip()) if arg_desc else []
arguments.append({
'name': arg_name,
'type': arg_refined_type,
'description': arg_desc,
'is_required': arg_required
})
return arguments
def process_examples(examples: str):
if not examples:
return []
examples = examples.strip()
splits = examples.split('\n')
result = []
inner = {}
for s in splits:
if not s:
continue
if '> curl' in s:
inner['curl'] = s.strip()
elif '> lbrycrd' in s:
inner['cli'] = s.strip()
else:
if 'title' in inner:
result.append(inner)
inner = {}
inner['title'] = s.strip()
result.append(inner)
return result
def get_api(section_name, command, command_help):
parsed = re_full.fullmatch(command_help)
if parsed is None:
raise Exception('Unable to resolve help format for ' + command)
name, desc, args, resl, exmp = parsed.group('name', 'desc', 'args', 'resl', 'exmp')
arguments = parse_params(args)
cmd_desc = re.sub('\s+', ' ', desc.strip()) if desc else ''
examp_desc = process_examples(exmp)
cmd_resl = resl.strip() if resl else None
ret = {
'name': command,
'namespace': section_name,
'description': cmd_desc,
'arguments': arguments,
'examples': examp_desc
}
if cmd_resl is not None:
ret['returns'] = cmd_resl
return ret
def write_api():
if len(sys.argv) < 2:
print("Missing required argument: <path to CLI tool>", file=sys.stderr)
sys.exit(1)
cli_tool = sys.argv[1]
result = sp.run([cli_tool, "help"], stdout=sp.PIPE, universal_newlines=True)
commands = result.stdout
sections = re.split('^==\s*(.*?)\s*==$', commands, flags=re.MULTILINE)
apis = []
for section in sections:
if not section:
continue
lines = section.splitlines()
if len(lines) == 1:
section_name = lines[0]
continue
for command in sorted(lines[1:]):
if not command:
continue
command = command.split(' ')[0]
result = sp.run([cli_tool, "help", command], stdout=sp.PIPE, universal_newlines=True)
apis.append(get_api(section_name, command, result.stdout))
print(json.dumps(apis, indent=4))
if __name__ == '__main__':
write_api()