ticket 1707 - add documentation validation to makeapi tool

Iterate over all API commands and perform the following validation:

* Every command must have documentation
  and it must be marked for international translation

* Every module hosting a command must have documentation
  and it must be marked for international translation

* Every module topic must be marked for international translation

For every error found emit a diagnostic.
Emit a summary of total errors found.

Return error flag if errors found, zero otherwise.
This commit is contained in:
John Dennis 2011-08-24 15:22:03 -04:00 committed by Rob Crittenden
parent 6dfd7c8242
commit f4ad749126

120
makeapi
View File

@ -1,6 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
# Authors: # Authors:
# Rob Crittenden <rcritten@redhat.com> # Rob Crittenden <rcritten@redhat.com>
# John Dennis <jdennis@redhat.com>
# #
# Copyright (C) 2011 Red Hat # Copyright (C) 2011 Red Hat
# see file 'COPYING' for use and warranty information # see file 'COPYING' for use and warranty information
@ -24,14 +25,16 @@
import sys import sys
import os import os
import re import re
import inspect
from ipalib import * from ipalib import *
from ipalib.text import Gettext from ipalib.text import Gettext, NGettext
API_FILE='API.txt' API_FILE='API.txt'
API_FILE_DIFFERENCE = 1 API_FILE_DIFFERENCE = 1
API_NEW_COMMAND = 2 API_NEW_COMMAND = 2
API_NO_FILE = 4 API_NO_FILE = 4
API_DOC_ERROR = 8
def parse_options(): def parse_options():
from optparse import OptionParser from optparse import OptionParser
@ -40,6 +43,9 @@ def parse_options():
parser.add_option("--validate", dest="validate", action="store_true", parser.add_option("--validate", dest="validate", action="store_true",
default=False, help="Validate the API vs the stored API") default=False, help="Validate the API vs the stored API")
parser.add_option("--no-validate-doc", dest="validate_doc", action="store_false",
default=True, help="Do not validate documentation")
options, args = parser.parse_args() options, args = parser.parse_args()
return options, args return options, args
@ -53,6 +59,104 @@ def strip_doc(line):
return newline return newline
def validate_doc():
"""
Iterate over all API commands and perform the following validation:
* Every command must have documentation
and it must be marked for international translation
* Every module hosting a command must have documentation
and it must be marked for international translation
* Every module topic must be marked for international translation
For every error found emit a diagnostic.
Emit a summary of total errors found.
Return error flag if errors found, zero otherwise.
"""
def is_i18n(obj):
'Helper utility to determine if object has been internationalized'
return isinstance(obj, (Gettext, NGettext))
# The return value
rval = 0
# Used to track if we've processed a module already
modules = {}
# Initialize error counters
n_missing_cmd_doc = 0
n_missing_cmd_i18n = 0
n_missing_mod_doc = 0
n_missing_mod_i18n = 0
# Iterate over every command
for cmd in api.Command():
cmd_class = cmd.__class__
# Skip commands marked as NO_CLI
if getattr(cmd, 'NO_CLI', False):
continue
# Have we processed this module yet?
if not modules.setdefault(cmd.module, 0):
# First time seeing this module, validate the module contents
mod = sys.modules[cmd.module]
# See if there is a module topic, if so validate it
topic = getattr(mod, 'topic', None)
if topic is not None:
if not is_i18n(topic[1]):
src_file = inspect.getsourcefile(cmd_class)
n_missing_mod_i18n += 1
print "%s: topic in module \"%s\" is not internationalized" % \
(src_file, cmd.module)
# Does the module have documentation?
if mod.__doc__ is None:
src_file = inspect.getsourcefile(mod)
n_missing_mod_doc += 1
print "%s: module \"%s\" has no doc" % \
(src_file, cmd.module)
# Yes the module has doc, but is it internationalized?
elif not is_i18n(mod.__doc__):
src_file = inspect.getsourcefile(cmd_class)
n_missing_mod_i18n += 1
print "%s: module \"%s\" doc is not internationalized" % \
(src_file, cmd.module)
# Increment the count of how many commands in this module
modules[cmd.module] = modules[cmd.module] + 1
# Does the command have documentation?
if cmd.__doc__ is None:
src_file = inspect.getsourcefile(cmd_class)
line_num = inspect.getsourcelines(cmd_class)[1]
n_missing_cmd_doc += 1
print "%s:%d command \"%s\" has no doc" % (src_file, line_num, cmd.name)
# Yes the command has doc, but is it internationalized?
elif not is_i18n(cmd.__doc__):
src_file = inspect.getsourcefile(cmd_class)
line_num = inspect.getsourcelines(cmd_class)[1]
n_missing_cmd_i18n += 1
print "%s:%d command \"%s\" doc is not internationalized" % (src_file, line_num, cmd.name)
# If any errors, emit summary information and adjust return value
if n_missing_cmd_doc > 0 or n_missing_cmd_i18n > 0:
rval = API_DOC_ERROR
print "%d commands without doc, %d commands whose doc is not i18n" % \
(n_missing_cmd_doc, n_missing_cmd_i18n)
if n_missing_mod_doc > 0 or n_missing_mod_i18n > 0:
rval = API_DOC_ERROR
print "%d modules without doc, %d modules whose doc is not i18n" % \
(n_missing_mod_doc, n_missing_mod_i18n)
return rval
def make_api(): def make_api():
""" """
Write a new API file from the current tree. Write a new API file from the current tree.
@ -242,6 +346,7 @@ def validate_api():
return rval return rval
def main(): def main():
rval = 0
options, args = parse_options() options, args = parse_options()
cfg = dict( cfg = dict(
@ -257,15 +362,18 @@ def main():
api.bootstrap(**cfg) api.bootstrap(**cfg)
api.finalize() api.finalize()
if options.validate_doc:
rval |= validate_doc()
if options.validate: if options.validate:
if not os.path.exists(API_FILE): if not os.path.exists(API_FILE):
print 'No %s to validate' % API_FILE print 'No %s to validate' % API_FILE
rval = API_NO_FILE rval |= API_NO_FILE
else: else:
rval = validate_api() rval |= validate_api()
else: else:
print "Writing API to API.txt" print "Writing API to API.txt"
rval = make_api() rval |= make_api()
if rval & API_FILE_DIFFERENCE: if rval & API_FILE_DIFFERENCE:
print '' print ''
@ -275,6 +383,10 @@ def main():
print '' print ''
print 'There are one or more new commands defined.\nUpdate API.txt and increment the minor version in VERSION.' print 'There are one or more new commands defined.\nUpdate API.txt and increment the minor version in VERSION.'
if rval & API_DOC_ERROR:
print ''
print 'There are one or more documentation problems.\nYou must fix these before preceeding'
return rval return rval
sys.exit(main()) sys.exit(main())