#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de
# Barcelona (UAB).
#
# This work is licensed under the terms of the MIT license.
# For a copy, see
'])
def note(self, buf):
self._data = join([self._data, buf])
def code_block(self, buf, language=''):
return join(['```', language, '\n', self.list_depth(), buf, '\n', self.list_depth(), '```\n'])
def prettify_doc(self, doc):
punctuation_marks = ['.', '!', '?']
doc = doc.strip()
doc += '' if doc[-1:] in punctuation_marks else '.'
return doc
def italic(buf):
return join(['_', buf, '_'])
def bold(buf):
return join(['**', buf, '**'])
def snipet(name,class_key):
return join(["'])
def code(buf):
return join(['`', buf, '`'])
def brackets(buf):
return join(['[', buf, ']'])
def parentheses(buf):
return join(['(', buf, ')'])
def small_html(buf):
return join([''+buf+''])
def small(buf):
return join(['', buf, ''])
def sub(buf):
return join(['', buf, ''])
def html_key(buf):
return join([''])
def color(col, buf):
return join(['', buf, ''])
def valid_dic_val(dic, value):
return value in dic and dic[value]
class YamlFile:
"""Yaml file class"""
def __init__(self, path):
self._path = path
with open(path) as yaml_file:
self.data = yaml.safe_load(yaml_file)
self.validate()
def validate(self):
# print('Validating ' + str(self._path.replace('\\', '/').split('/')[-1:][0]))
if self.data is None:
print('\n[ERROR] File: ' + self._path)
print("This file has no data:")
exit(0)
for module in self.data:
if 'module_name' in module and module['module_name'] is None:
print('\n[ERROR] File: ' + self._path)
print("'module_name' is empty in:")
exit(0)
if 'classes' in module:
if not module['classes']:
print('\n[ERROR] File: ' + self._path)
print("'classes' is empty in:")
exit(0)
for cl in module['classes']:
if 'class_name' in cl and cl['class_name'] is None:
print('\n[ERROR] File: ' + self._path)
print("'class_name' is empty in:")
exit(0)
if 'instance_variables' in cl and cl['instance_variables']:
for iv in cl['instance_variables']:
if 'var_name' not in iv:
print('\n[ERROR] File: ' + self._path)
print("'var_name' not found inside 'instance_variables' of class: " + cl['class_name'])
exit(0)
if 'var_name' in iv and iv['var_name'] is None:
print('\n[ERROR] File: ' + self._path)
print("'var_name' is empty in:")
exit(0)
if 'methods' in cl and cl['methods']:
for met in cl['methods']:
if 'def_name' not in met:
print('\n[ERROR] File: ' + self._path)
print("'def_name' not found inside 'methods' of class: " + cl['class_name'])
exit(0)
if 'def_name' in met and met['def_name'] is None:
print('\n[ERROR] File: ' + self._path)
print("'def_name' is empty in:")
exit(0)
if 'params' in met and met['params']:
for param in met['params']:
if 'param_name' not in param:
print('\n[ERROR] File: ' + self._path)
print("'param_name' not found inside 'params' of class: " + cl['class_name'])
exit(0)
if 'param_name' in param and param['param_name'] is None:
print('\n[ERROR] File: ' + self._path)
print("'param_name' is empty in:")
exit(0)
if 'type' in param and param['type'] is None:
print('\n[ERROR] File: ' + self._path)
print("'type' is empty in:")
exit(0)
def get_modules(self):
return [module for module in self.data]
def append_snipet_button_script(md):
md.textn("\n\n\n")
def append_code_snipets(md):
current_folder = os.path.dirname(os.path.abspath(__file__))
snipets_path = os.path.join(current_folder, '../../Docs/python_api_snipets.md')
snipets = open(snipets_path, 'r')
md.text(snipets.read())
os.remove(snipets_path)
def gen_stub_method_def(method):
"""Return python def as it should be written in stub files"""
param = ''
method_name = method['def_name']
for p in method['params']:
p_type = join([': ', str(p['type'])]) if 'type' in p else ''
default = join([' = ', str(p['default'])]) if 'default' in p else ''
param = join([param, p['param_name'], p_type, default, ', '])
param = param[:-2] # delete the last ', '
return_type = join([' -> ', method['return']]) if 'return' in method else ''
return join([method_name, parentheses(param), return_type])
def gen_doc_method_def(method, class_key, is_indx=False, with_self=True):
"""Return python def as it should be written in docs"""
param = ''
snipet_link = ''
method_name = method['def_name']
full_method_name = method_name
if valid_dic_val(method, 'static'):
with_self = False
# to correctly render methods like __init__ in md
if method_name[0] == '_':
method_name = '\\' + method_name
if is_indx:
method_name = bold(method_name)
else:
method_name = bold(color(COLOR_METHOD, method_name))
if with_self:
if not 'params' in method or method['params'] is None:
method['params'] = []
method['params'].insert(0, {'param_name': 'self'})
if valid_dic_val(method, 'params'):
for p in method['params']:
default = join(['=', str(p['default'])]) if 'default' in p else ''
if is_indx:
param = join([param, bold(p['param_name']), default, ', '])
else:
param = join([param, color(COLOR_PARAM, bold(p['param_name']) + create_hyperlinks(default)), ', '])
if with_self:
method['params'] = method['params'][1:]
if not method['params']: # if is empty delete it
del method['params']
param = param[:-2] # delete the last ', '
# Add snipet
current_folder = os.path.dirname(os.path.abspath(__file__))
snipets_path = os.path.join(current_folder, '../../Docs/python_api_snipets.md')
snipets = open(snipets_path, 'r')
if class_key+'.'+full_method_name+'-snipet' in snipets.read():
snipet_link = snipet(full_method_name, class_key)
return join([method_name, parentheses(param),snipet_link])
def gen_doc_dunder_def(dunder, is_indx=False, with_self=True):
"""Return python def as it should be written in docs"""
param = ''
dunder_name = dunder['def_name']
if valid_dic_val(dunder, 'static'):
with_self = False
# to correctly render methods like __init__ in md
if dunder_name[0] == '_':
dunder_name = '\\' + dunder_name
if is_indx:
dunder_name = bold(dunder_name)
else:
dunder_name = bold(color(COLOR_METHOD, dunder_name))
if with_self:
if not 'params' in dunder or dunder['params'] is None:
dunder['params'] = []
dunder['params'].insert(0, {'param_name': 'self'})
if valid_dic_val(dunder, 'params'):
for p in dunder['params']:
default = join(['=', str(p['type'])]) if 'type' in p else ''
if is_indx:
param = join([param, bold(p['param_name']), default, ', '])
else:
param = join([param, color(COLOR_PARAM, bold(p['param_name']) + create_hyperlinks(default)), ', '])
if with_self:
dunder['params'] = dunder['params'][1:]
if not dunder['params']: # if is empty delete it
del dunder['params']
param = param[:-2] # delete the last ', '
return join([dunder_name, parentheses(param)])
def gen_inst_var_indx(inst_var, class_key):
inst_var_name = inst_var['var_name']
inst_var_key = join([class_key, inst_var_name], '.')
return join([
brackets(bold(inst_var_name)),
parentheses(inst_var_key), ' ',
sub(italic('Instance variable'))])
def gen_method_indx(method, class_key):
method_name = method['def_name']
method_key = join([class_key, method_name], '.')
method_def = gen_doc_method_def(method, class_key, True)
return join([
brackets(method_def),
parentheses(method_key), ' ',
sub(italic('Method'))])
def add_doc_method_param(md, param):
param_name = param['param_name']
param_type = ''
param_doc = ''
param_units = ''
if valid_dic_val(param, 'type'):
param_type = create_hyperlinks(param['type'])
if valid_dic_val(param, 'doc'):
param_doc = create_hyperlinks(md.prettify_doc(param['doc']))
if valid_dic_val(param, 'param_units'):
param_units = small_html(' – '+param['param_units'])
param_type = '' if not param_type else parentheses(italic(param_type+param_units))
md.list_push(code(param_name))
if param_type:
md.text(' ' + param_type)
if param_doc:
md.textn(' – ' + param_doc)
else:
md.new_line()
md.list_pop()
def add_doc_method(md, method, class_key):
method_name = method['def_name']
method_key = join([class_key, method_name], '.')
method_def = gen_doc_method_def(method, class_key, False)
md.list_pushn(join([html_key(method_key), method_def]))
# Method doc
if valid_dic_val(method, 'doc'):
md.textn(create_hyperlinks(md.prettify_doc(method['doc'])))
printed_title = False
if valid_dic_val(method, 'params'):
for param in method['params']:
# is_self = valid_dic_val(param, 'param_name') and param['param_name'] == 'self'
have_doc = valid_dic_val(param, 'doc')
have_type = valid_dic_val(param, 'type')
if not have_doc and not have_type:
continue
# Print the 'Parameters' title once
if not printed_title:
printed_title = True
md.list_push(bold('Parameters:') + '\n')
add_doc_method_param(md, param)
if printed_title:
md.list_pop()
# Return doc
if valid_dic_val(method, 'return'):
md.list_push(bold('Return:') + ' ')
return_units = ''
if valid_dic_val(method, 'return_units'):
return_units = small_html(' – '+method['return_units'])
md.textn(italic(create_hyperlinks(method['return'])+return_units))
md.list_pop()
# Note doc
if valid_dic_val(method, 'note'):
md.list_push(bold('Note:') + ' ')
md.textn(color(COLOR_NOTE, italic(create_hyperlinks(method['note']))))
md.list_pop()
# Warning doc
if valid_dic_val(method, 'warning'):
md.list_push(bold('Warning:') + ' ')
md.textn(color(COLOR_WARNING, italic(create_hyperlinks(method['warning']))))
md.list_pop()
# Raises error doc
if valid_dic_val(method, 'raises'):
md.list_pushn(bold('Raises:') + ' ' + method['raises'])
md.list_pop()
md.list_pop()
def add_doc_getter_setter(md, method, class_key, is_getter, other_list):
method_name = method['def_name']
method_key = join([class_key, method_name], '.')
method_def = gen_doc_method_def(method, class_key, False)
md.list_pushn(join([html_key(method_key), method_def]))
# Method doc
if valid_dic_val(method, 'doc'):
md.textn(create_hyperlinks(md.prettify_doc(method['doc'])))
printed_title = False
if valid_dic_val(method, 'params'):
for param in method['params']:
# is_self = valid_dic_val(param, 'param_name') and param['param_name'] == 'self'
have_doc = valid_dic_val(param, 'doc')
have_type = valid_dic_val(param, 'type')
if not have_doc and not have_type:
continue
# Print the 'Parameters' title once
if not printed_title:
printed_title = True
md.list_push(bold('Parameters:') + '\n')
add_doc_method_param(md, param)
if printed_title:
md.list_pop()
# Return doc
if valid_dic_val(method, 'return'):
md.list_push(bold('Return:') + ' ')
return_units = ''
if valid_dic_val(method, 'return_units'):
return_units = small_html(' – '+method['return_units'])
md.textn(italic(create_hyperlinks(method['return'])+return_units))
md.list_pop()
# If setter/getter
for element in other_list:
el_name = element['def_name']
if el_name[4:] == method_name[4:]:
if is_getter:
md.list_push(bold('Setter:') + ' ')
else:
md.list_push(bold('Getter:') + ' ')
md.textn(italic(create_hyperlinks(class_key+'.'+el_name)))
md.list_pop()
# Note doc
if valid_dic_val(method, 'note'):
md.list_push(bold('Note:') + ' ')
md.textn(color(COLOR_NOTE, italic(create_hyperlinks(method['note']))))
md.list_pop()
# Warning doc
if valid_dic_val(method, 'warning'):
md.list_push(bold('Warning:') + ' ')
md.textn(color(COLOR_WARNING, italic(create_hyperlinks(method['warning']))))
md.list_pop()
# Raises error doc
if valid_dic_val(method, 'raises'):
md.list_pushn(bold('Raises:') + ' ' + method['raises'])
md.list_pop()
md.list_pop()
def add_doc_dunder(md, dunder, class_key):
dunder_name = dunder['def_name']
dunder_key = join([class_key, dunder_name], '.')
dunder_def = gen_doc_dunder_def(dunder, False)
md.list_pushn(join([html_key(dunder_key), dunder_def]))
# Dunder doc
if valid_dic_val(dunder, 'doc'):
md.textn(create_hyperlinks(md.prettify_doc(dunder['doc'])))
# Return doc
if valid_dic_val(dunder, 'return'):
md.list_push(bold('Return:') + ' ')
md.textn(italic(create_hyperlinks(dunder['return'])))
md.list_pop()
md.list_pop()
def add_doc_dunder_param(md, param):
param_name = param['param_name']
param_type = ''
if valid_dic_val(param, 'type'):
param_type = create_hyperlinks(param['type'])
param_type = '' if not param_type else parentheses(italic(param_type))
md.list_push(code(param_name))
if param_type:
md.text(' ' + param_type)
md.new_line()
else:
md.new_line()
md.list_pop()
def add_doc_inst_var(md, inst_var, class_key):
var_name = inst_var['var_name']
var_key = join([class_key, var_name], '.')
var_type = ''
var_units = ''
# Instance variable type
if valid_dic_val(inst_var, 'type'):
if valid_dic_val(inst_var, 'var_units'):
var_units = small_html(' – '+inst_var['var_units'])
var_type = ' ' + parentheses(italic(create_hyperlinks(inst_var['type']+var_units)))
md.list_pushn(
html_key(var_key) +
bold(color(COLOR_INSTANCE_VAR, var_name)) +
var_type)
# Instance variable doc
if valid_dic_val(inst_var, 'doc'):
md.textn(create_hyperlinks(md.prettify_doc(inst_var['doc'])))
# Note doc
if valid_dic_val(inst_var, 'note'):
md.list_push(bold('Note:') + ' ')
md.textn(color(COLOR_NOTE, italic(create_hyperlinks(inst_var['note']))))
md.list_pop()
# Warning doc
if valid_dic_val(inst_var, 'warning'):
md.list_push(bold('Warning:') + ' ')
md.textn(color(COLOR_WARNING, italic(create_hyperlinks(inst_var['warning']))))
md.list_pop()
md.list_pop()
class Documentation:
"""Main documentation class"""
def __init__(self, path):
self._path = path
self._files = [f for f in os.listdir(path) if f.endswith('.yml')]
self._yamls = list()
for yaml_file in self._files:
self._yamls.append(YamlFile(os.path.join(path, yaml_file)))
# Merge same modules of different files
self.master_dict = dict()
for yaml_file in self._yamls:
for module in yaml_file.get_modules():
module_name = module['module_name']
if module_name not in self.master_dict:
self.master_dict[module_name] = module
elif valid_dic_val(module, 'classes'):
for new_module in module['classes']:
# Create the 'classes' key if does not exist already
if not valid_dic_val(self.master_dict[module_name], 'classes'):
self.master_dict[module_name]['classes'] = []
self.master_dict[module_name]['classes'].append(new_module)
def gen_overview(self):
"""Generates a referenced index for markdown file"""
md = MarkdownFile()
md.title(3, 'Overview')
for module_name in sorted(self.master_dict):
module = self.master_dict[module_name]
module_key = '#' + module_name
md.list_pushn(
brackets(bold(module_key[1:])) +
parentheses(module_key) + ' ' +
sub(italic('Module')))
# Generate class overview (if any)
if 'classes' in module and module['classes']:
for cl in sorted(module['classes']):
class_name = cl['class_name']
class_key = join([module_key, class_name], '.')
md.list_pushn(join([
brackets(bold(class_name)),
parentheses(class_key), ' ',
sub(italic('Class'))]))
# Generate class instance variables overview (if any)
if 'instance_variables' in cl and cl['instance_variables']:
for inst_var in cl['instance_variables']:
md.list_push(gen_inst_var_indx(inst_var, class_key))
md.list_popn()
# Generate class methods overview (if any)
if 'methods' in cl and cl['methods']:
for method in sorted(cl['methods'], key = lambda i: i['def_name']):
md.list_push(gen_method_indx(method, class_key))
md.list_popn()
md.list_pop()
md.list_pop()
return md.data()
def gen_body(self):
"""Generates the documentation body"""
md = MarkdownFile()
md.first_title()
md.textn(
"This reference contains all the details the Python API. To consult a previous reference for a specific CARLA release, change the documentation version using the panel in the bottom right corner.
"
+"This will change the whole documentation to a previous state. Remember to go back to latest to get the details of the current state of CARLA.