from __future__ import print_function import sys import logging import os import re from pprint import pprint import traceback try: import bs4 from bs4 import BeautifulSoup except ImportError: raise ImportError('Error: ' 'Install BeautifulSoup (bs4) for adding' ' Python & Java signatures documentation') def load_html_file(file_dir): """ Uses BeautifulSoup to load an html """ with open(file_dir, 'rb') as fp: data = fp.read() if os.name == 'nt' or sys.version_info[0] == 3: data = data.decode(encoding='utf-8', errors='strict') data = re.sub(r'(\>)([ ]+)', lambda match: match.group(1) + ('!space!' * len(match.group(2))), data) data = re.sub(r'([ ]+)(\<)', lambda match: ('!space!' * len(match.group(1))) + match.group(2), data) if os.name == 'nt' or sys.version_info[0] == 3: data = data.encode('utf-8', 'ignore') soup = BeautifulSoup(data, 'html.parser') return soup def update_html(file, soup): s = str(soup) s = s.replace('!space!', ' ') if os.name == 'nt' or sys.version_info[0] == 3: s = s.encode('utf-8', 'ignore') with open(file, 'wb') as f: f.write(s) def insert_python_signatures(python_signatures, symbols_dict, filepath): soup = load_html_file(filepath) entries = soup.find_all(lambda tag: tag.name == "a" and tag.has_attr('id')) for e in entries: anchor = e['id'] if anchor in symbols_dict: s = symbols_dict[anchor] logging.info('Process: %r' % s) if s.type == 'fn' or s.type == 'method': process_fn(soup, e, python_signatures[s.cppname], s) elif s.type == 'const': process_const(soup, e, python_signatures[s.cppname], s) else: logging.error('unsupported type: %s' % s); update_html(filepath, soup) def process_fn(soup, anchor, python_signature, symbol): try: r = anchor.find_next_sibling(class_='memitem').find(class_='memproto').find('table') insert_python_fn_signature(soup, r, python_signature, symbol) except: logging.error("Can't process: %s" % symbol) traceback.print_exc() pprint(anchor) def process_const(soup, anchor, python_signature, symbol): try: #pprint(anchor.parent) description = append(soup.new_tag('div', **{'class' : ['python_language']}), 'Python: ' + python_signature[0]['name']) old = anchor.find_next_sibling('div', class_='python_language') if old is None: anchor.parent.append(description) else: old.replace_with(description) #pprint(anchor.parent) except: logging.error("Can't process: %s" % symbol) traceback.print_exc() pprint(anchor) def insert_python_fn_signature(soup, table, variants, symbol): description = create_python_fn_description(soup, variants) description['class'] = 'python_language' soup = insert_or_replace(table, description, 'table', 'python_language') return soup def create_python_fn_description(soup, variants): language = 'Python:' table = soup.new_tag('table') heading_row = soup.new_tag('th') table.append( append(soup.new_tag('tr'), append(soup.new_tag('th', colspan=999, style="text-align:left"), language))) for v in variants: #logging.debug(v) add_signature_to_table(soup, table, v, language, type) #print(table) return table def add_signature_to_table(soup, table, signature, language, type): """ Add a signature to an html table""" row = soup.new_tag('tr') row.append(soup.new_tag('td', style='width: 20px;')) row.append(append(soup.new_tag('td'), signature['name'] + '(')) row.append(append(soup.new_tag('td', **{'class': 'paramname'}), signature['arg'])) row.append(append(soup.new_tag('td'), ') -> ')) row.append(append(soup.new_tag('td'), signature['ret'])) table.append(row) def append(target, obj): target.append(obj) return target def insert_or_replace(element_before, new_element, tag, tag_class): old = element_before.find_next_sibling(tag, class_=tag_class) if old is None: element_before.insert_after(new_element) else: old.replace_with(new_element)