Skip to content

Port API coverage reporting to Python 3 and adapt to current doxygen output #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 72 additions & 69 deletions doc/APICoverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA


import os, pickle, urllib, sys
import os, pickle, urllib.request, urllib.parse, urllib.error, sys
from pyparsing import *
from htmlentitydefs import entitydefs
from htmllib import HTMLParser
from html.entities import entitydefs
from html.parser import HTMLParser
from formatter import AbstractFormatter, DumbWriter

# cache dir (preparsed source and HTML asoundlib API)
Expand All @@ -40,7 +40,7 @@
count_total = count_miss = count_exc = 0

if not os.path.exists(cache):
print "Creating cache dir: %s" % cache
print("Creating cache dir: %s" % cache)
os.makedirs(cache)

def read_source(name):
Expand Down Expand Up @@ -72,20 +72,20 @@ def get_cached_parse(index_parser_list, name):
if os.stat(source[2]).st_mtime > os.stat(name).st_mtime:
modified = True
if not modified:
return pickle.load(open(name))
return pickle.load(open(name, "rb"))

print "generating cache, file: %s" % name,
print("generating cache, file: %s" % name, end=' ')
dict = {}
for source, parser in index_parser_list:
print source[0],
print(source[0], end=' ')
sys.stdout.flush()
list = []
for tokenlist, start, end in parser.scanString(source[1]):
tlist = list_to_str(tokenlist)
list.append((tlist, int(start), int(end)))
dict[source[0]] = list
pickle.dump(dict, open(name, "wb"))
print
print()
return dict

# API html parsing/caching
Expand All @@ -100,8 +100,9 @@ def get_cached_api(url, name):
if os.path.exists(name):
data = "".join(open(name).readlines())
else:
print "downloading %s" % url
data = urllib.urlopen(url).read()
print("downloading %s" % url)
# The ALSA project API web pages use UTF-8
data = str(urllib.request.urlopen(url).read().decode('utf-8'))
open(name, "w").write(data)
return data

Expand All @@ -113,29 +114,24 @@ class AsoundlibAPIHTMLParser(HTMLParser):
HTML asoundlib API from the alsa website.
"""

HTMLParser.entitydefs['nbsp'] = ' '

def __init__(self, name, data):
f = AbstractFormatter(DumbWriter(open(name, 'w'), 100))
HTMLParser.__init__(self, f)
self.f = AbstractFormatter(DumbWriter(open(name, 'w'), 100))
HTMLParser.__init__(self)
self.feed(data)
self.close()

def start_h1(self, attrs):
HTMLParser.start_h1(self, attrs)
self.handle_data("--- titlestart")
self.do_br(None)

def start_table(self, attrs):
if len(attrs) == 1 and attrs[0] == ("class", "memname"):
self.handle_data("--- itemstart")
self.do_br(None)
def handle_data(self, data):
self.f.add_literal_data(data)

def start_tr(self, attrs):
self.do_br(None)

def anchor_end(self):
pass
def handle_starttag(self, tag, attrs):
if tag == "div":
if len(attrs) == 1 and attrs[0] == ("class", "title"):
self.handle_data("\n--- titlestart\n")
if len(attrs) == 1 and attrs[0] == ("class", "ingroups"):
self.handle_data("\n\n")
elif tag == 'table':
if len(attrs) == 1 and attrs[0] == ("class", "memname"):
self.handle_data("\n--- itemstart")

def parse_asoundlib_api(lines):
"""
Expand All @@ -153,21 +149,22 @@ def parse_asoundlib_api(lines):
comment = ""
enumsublist = []
for line in lines:
line = line[:-1]
# convert   to space
line = line[:-1].replace('\xa0', ' ')
if False:
if id(current) == id(defines):
print "defines ",
print("defines ", end=' ')
elif id(current) == id(typedefs):
print "typedefs ",
print("typedefs ", end=' ')
elif id(current) == id(enums):
print "enums ",
print("enums ", end=' ')
elif id(current) == id(functions):
print "functions ",
print("functions ", end=' ')
else:
print " ",
print "%s %d %s" % (id(current), state, line)
print(" ", end=' ')
print("%s %d %s" % (id(current), state, line))

if line.startswith('Define Documentation'):
if line.startswith('Macro Definition Documentation'):
current = defines
state = 0
elif line.startswith('Typedef Documentation'):
Expand All @@ -184,36 +181,42 @@ def parse_asoundlib_api(lines):
elif line.startswith('--- titlestart'):
state = 5
elif state == 5:
title = line
title = line.strip()
state = 0
elif current == None:
continue
elif state == 1:
if line == "":
name = ' '.join(name.split())
state = 2
else:
name += line
elif state == 2:
elif state == 2 and line != "":
comment = line.strip()
if id(current) == id(enums):
state = 3
else:
comment = line
current.append((name, comment))
name = ""
comment = ""
state = 0
elif state == 3 and line.startswith('Enumerator:'):
elif state == 3 and line.startswith('Enumerator'):
enum, subcomment = line[10:].split(' ', 1)
enumsublist = [(enum.strip(), subcomment.strip())]
linewasempty = False
state = 4
enumsublist = []
elif state == 4:
if line == "":
if linewasempty and line == "":
current.append((name, comment, enumsublist))
name = ""
comment = ""
state = 0
elif line == "":
linewasempty = True
else:
enum, subcomment = line.split(' ', 1)
enumsublist.append((enum, subcomment))
enumsublist.append((enum.strip(), subcomment.strip()))
linewasempty = False

return (title, defines, typedefs, enums, functions)

Expand Down Expand Up @@ -253,22 +256,22 @@ def print_name(d0, d1, name, look_constant, look_usage, exclude_list):
else:
used = "%s" % usecount

print "%-4s%s" % (used, d0)
print "%8s%s" % ("", d1)
print("%-4s%s" % (used, d0))
print("%8s%s" % ("", d1))

if usecount > 0:
excstr = "Comment"
else:
excstr = "Excluded"
for token, comment in exclude_list:
if token == name:
print "%10s==> %11s: %s" % ("", excstr, comment)
print("%10s==> %11s: %s" % ("", excstr, comment))
for s in lc:
print "%10s=> As constant: %s" % ("", s)
print("%10s=> As constant: %s" % ("", s))
for s in uc:
print "%10s=> Used in : %s" % ("", s)
print("%10s=> Used in : %s" % ("", s))
if used == "N/A":
print " "*10 + "**** NOT AVAILABLE/USED %s ****" % name
print(" "*10 + "**** NOT AVAILABLE/USED %s ****" % name)


def _print_stat(title, section, missing, excluded, total):
Expand All @@ -283,9 +286,9 @@ def _print_stat(title, section, missing, excluded, total):
fmissing = "%3.0f%%" % fmissing
fexcluded = "%3.0f%%" % fexcluded
fcovered = "%3.0f%%" % fcovered
print "STAT %-30.30s %-12.12s: " % (title, section) + \
print("STAT %-30.30s %-12.12s: " % (title, section) + \
"%3d missing (%4s) %3d excluded (%4s) of %3d total (%4s covered)." % \
(missing, fmissing, excluded, fexcluded, total, fcovered)
(missing, fmissing, excluded, fexcluded, total, fcovered))


def print_stat(title, section):
Expand Down Expand Up @@ -329,46 +332,46 @@ def print_api_coverage(urls, look_constant, look_usage, excludes):
AsoundlibAPIHTMLParser(tmp, data)
(title, defines, typedefs, enums, functions) = \
parse_asoundlib_api(open(tmp).readlines())
print title
print "="*len(title)
print "\n"*2
print(title)
print("="*len(title))
print("\n"*2)
#print "%s\n%s\n%s\n%s\n%s\n\n" % \
# (title, defines, typedefs, enums, functions)
summary_total = 0
summary_miss = 0
if len(defines) > 0:
print "Defines"
print "-------"
print("Defines")
print("-------")
for d in defines:
name = d[0].split(' ')[1]
print_name(d[0], d[1], name, look_constant, look_usage, el)
print_stat(title, "Defines")
print "\n"*2
print("\n"*2)
if len(typedefs) > 0:
print "Typedefs"
print "--------"
print("Typedefs")
print("--------")
for d in typedefs:
names = d[0].split(' ')
name = names[-1]
if ')' in name:
names = d[0].split('(')
name = names[-2].split()[-1]
name = names[-2].removesuffix(') ').removeprefix('* ')
print_name(d[0], d[1], name, look_constant, look_usage, el)
print_stat(title, "Typedefs")
print "\n"*2
print("\n"*2)
if len(enums) > 0:
print "Enumerations"
print "------------"
print("Enumerations")
print("------------")
for e in enums:
print "%s" % e[0]
print("%s" % e[0])
for d in e[2]:
name = d[0]
print_name(d[0], d[1], name, look_constant, look_usage, el)
print_stat(title, "Enumerations")
print "\n"*2
print("\n"*2)
if len(functions) > 0:
print "Functions"
print "---------"
print("Functions")
print("---------")
for d in functions:
name = None
for n in d[0].split(' '):
Expand All @@ -379,7 +382,7 @@ def print_api_coverage(urls, look_constant, look_usage, excludes):
if name != None:
print_name(d[0], d[1], name, look_constant, look_usage, el)
print_stat(title, "Functions")
print "\n"*2
print("\n"*2)
print_summary_stat(title)
print "\n"*4
print("\n"*4)

25 changes: 13 additions & 12 deletions doc/alsa-python-coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import APICoverage
from pprint import pprint
import time
import getpass

__author__ = "Aldrin Martoq <amartoq@dcc.uchile.cl>"
__version__ = "1.0"
Expand Down Expand Up @@ -56,11 +57,11 @@
# + Group(("(" + args + Optional("...") + ")") | "{")

# common asoundlib parser
snd_ = Regex("snd_[\w_]+")
SND_ = Regex("SND_[\w_]+")
snd_ = Regex("snd_[\\w_]+")
SND_ = Regex("SND_[\\w_]+")

# pyalsa/alsaseq.c parser
alsaseq_SEQ = Regex("SEQ_[\w_]+")
alsaseq_SEQ = Regex('SEQ_[\\w_]+')
alsaseq_Constant = \
"TCONSTADD(module," + alsaseq_SEQ + "," + quotedString + "," + alsaseq_SEQ
alsaseq_typedef = \
Expand Down Expand Up @@ -229,15 +230,15 @@ def look_usage(name):
rs = tokens[0]
if rs == name:
list.append(start)
if dict.has_key(file):
if file in dict:
dict[file].extend(list)
else:
dict[file] = list

nlist = []
for file in dict:
for lstart in dict[file]:
if not index.has_key(file):
if file not in index:
continue
found = None
previous = None
Expand Down Expand Up @@ -314,7 +315,7 @@ def look_usage(name):
"""


print """
print("""
*******************************
PYALSA/ASOUNDLIB COVERAGE/USAGE
*******************************
Expand All @@ -338,11 +339,11 @@ def look_usage(name):
* EXC if the item is excluded (will not be used/implemented)
* The next line is the one-liner documentation.
* Following lines are usage/definition until next item.
* There are lines that summaries the coverage, they start with '^STAT'.
* There are lines that summarizes the coverage, they start with '^STAT'.



"""
""")


print_api_coverage(urls, look_constant, look_usage, comments)
Expand All @@ -351,14 +352,14 @@ def look_usage(name):
time_end = time.time()
time_diff = time_end - time_start

print """%s
print("""%s
Generated for ALSA project by alsa-python-coverage.py %s
%s UTC (%s@%s %3.3f seconds).
""" % ("-"*72,
__version__,
time.asctime(time.gmtime(time_start)),
os.getlogin(),
getpass.getuser(),
os.uname()[1],
time_diff
)
print
))
print()
Loading