Skip to content
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

Timob 7419 -Supported partially inherited documentation #2393

Merged
merged 3 commits into from
Jun 15, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 48 additions & 1 deletion apidoc/docgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
current_api = None
ignore_dirs = (".git", ".svn", "CVS")
ignore_files = ("template.yml",)
warn_inherited = False # see optparse option with same name in main()

def has_ancestor(one_type, ancestor_name):
if one_type["name"] == ancestor_name:
Expand Down Expand Up @@ -185,6 +186,43 @@ def process_yaml():
log.warn("%s has a duplicate" % one_type["name"])
apis[one_type["name"]] = one_type

# If documentation for a method/event/property only "partially overrides"
# the documentation for the super type, this will fill in the rest of
# the documentation by inheriting it from the super type.
def finish_partial_overrides():
global apis
log.trace("Finishing partial overrides")
for name in apis:
one_api = apis[name]
if "extends" not in one_api or not one_api["extends"]:
continue
super_name = one_api["extends"]
if super_name not in apis:
continue
log.trace("Checking partial overrides in %s by looking at %s" % (name, super_name))
super_api = apis[super_name]
for list_name in ("events", "properties", "methods"):
if list_name not in one_api or list_name not in super_api:
continue
api_list = one_api[list_name]
super_list = super_api[list_name]
for api_list_member in api_list:
member_name = api_list_member["name"]
super_list_member = None
super_member_set = [m for m in super_list if m["name"] == member_name]
if not super_member_set:
continue
super_list_member = super_member_set[0]
for key in super_list_member.keys():
if super_list_member[key] and (key not in api_list_member.keys() or not api_list_member[key]):
api_list_member[key] = super_list_member[key]
message = "%s.%s auto-inheriting '%s' documentation attribute from %s.%s" % (
one_api["name"], member_name, key, super_name, member_name)
if warn_inherited:
log.warn(message)
else:
log.trace(message)

def annotate_apis():
global apis, annotated_apis
log.trace("Annotating api objects")
Expand Down Expand Up @@ -503,7 +541,7 @@ def properties(self):
return sorted(properties, key=lambda item: item.name)

def main():
global this_dir, log
global this_dir, log, warn_inherited
titanium_dir = os.path.dirname(this_dir)
dist_apidoc_dir = os.path.join(titanium_dir, "dist", "apidoc")
sys.path.append(os.path.join(titanium_dir, "build"))
Expand Down Expand Up @@ -541,16 +579,25 @@ def main():
action="store_true",
help="Useful only for json/jsca. Writes the result to stdout. If you specify both --stdout and --output you'll get both an output file and the result will be written to stdout.",
default=False)
parser.add_option("--warn-inherited",
dest="warn_inherited",
action="store_true",
help="Show a warning if the documentation for a method/property/event only partially overrides its super type's documentation, in which case the missing information is inherited from the super type documentation.",
default=False)
(options, args) = parser.parse_args()
warn_inherited = options.warn_inherited
log_level = TiLogger.INFO
if options.verbose:
log_level = TiLogger.TRACE
log = TiLogger(None, level=log_level, output_stream=sys.stderr)
if options.output is None and "html" in options.formats:
log.trace("Setting output folder to %s because html files will be generated and now --output folder was specified" % dist_apidoc_dir)
options.output = dist_apidoc_dir

process_yaml()
finish_partial_overrides()
generate_output(options)

titanium_apis = [ta for ta in apis.values() if ta["name"].startswith("Ti")]
log.info("%s Titanium types processed" % len(titanium_apis))

Expand Down
87 changes: 78 additions & 9 deletions apidoc/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
}

types = {}
typesFromDocgen = None
errorTrackers = {}
options = None

Expand Down Expand Up @@ -117,8 +118,17 @@ def printError(self, msg):
self.error_count += 1

class ErrorTracker(object):
def __init__(self, name, parent=None):
TRACKER_FOR_TYPE = 0
TRACKER_FOR_METHOD = 1
TRACKER_FOR_PROPERTY = 2
TRACKER_FOR_EVENT = 3
TRACKER_FOR_METHOD_PARAMETER = 4
TRACKER_FOR_EVENT_PROPERTY = 5
TRACKER_FOR_REF = 5

def __init__(self, name, trackerFor, parent=None):
self.name = name
self.trackerFor = trackerFor
self.errors = []
self.children = []
self.parent = parent
Expand Down Expand Up @@ -154,9 +164,51 @@ def validateKeys(tracker, obj, objType):
if invalid:
tracker.trackError("Invalid key(s) in %s: %s" % (objName, invalid))

# A missing piece of documentation could be inherited, since we
# support that as-of TIMOB-7419. This checks to see if its there
# after all inherited documentation has been resolved.
def propertyIsGenerated(tracker, propertyName):
parent = tracker.parent
if not parent:
return False

while parent.parent:
parent = parent.parent

if parent.trackerFor != ErrorTracker.TRACKER_FOR_TYPE:
return False

typeName = parent.name

if typeName not in typesFromDocgen:
return False

generatedType = typesFromDocgen[typeName]

memberToCheck = None
listType = None

if tracker.trackerFor == ErrorTracker.TRACKER_FOR_METHOD:
listType = "methods"
elif tracker.trackerFor == ErrorTracker.TRACKER_FOR_PROPERTY:
listType = "properties"
elif tracker.trackerFor == ErrorTracker.TRACKER_FOR_EVENT:
listType = "events"

if not memberToCheck and listType:
the_list = generatedType[listType]
matching_members = [m for m in the_list if m["name"] == tracker.name]
if matching_members:
memberToCheck = matching_members[0]

if not memberToCheck:
return False
else:
return propertyName in memberToCheck

def validateRequired(tracker, map, required):
for r in required:
if r not in map:
if r not in map and not propertyIsGenerated(tracker, r):
tracker.trackError('Required property "%s" not found' % r)

def validatePlatforms(tracker, platforms):
Expand Down Expand Up @@ -278,7 +330,7 @@ def validateCommon(tracker, map):


def validateMethod(typeTracker, method):
tracker = ErrorTracker(method['name'], typeTracker)
tracker = ErrorTracker(method['name'], ErrorTracker.TRACKER_FOR_METHOD, typeTracker)
validateKeys(tracker, method, "method")
validateRequired(tracker, method, ['name', 'summary'])
validateCommon(tracker, method)
Expand All @@ -304,7 +356,7 @@ def validateMethod(typeTracker, method):
if type(method['parameters']) != list:
tracker.trackError('"parameters" must be a list')
for param in method['parameters']:
pTracker = ErrorTracker(param['name'], tracker)
pTracker = ErrorTracker(param['name'], ErrorTracker.TRACKER_FOR_METHOD_PARAMETER, tracker)
validateKeys(pTracker, param, "parameter")
validateRequired(pTracker, param, ['name', 'summary', 'type'])
validateCommon(pTracker, param)
Expand All @@ -313,7 +365,7 @@ def validateMethod(typeTracker, method):
validateExamples(tracker, method['examples'])

def validateProperty(typeTracker, property):
tracker = ErrorTracker(property['name'], typeTracker)
tracker = ErrorTracker(property['name'], ErrorTracker.TRACKER_FOR_PROPERTY, typeTracker)
validateKeys(tracker, property, "property")

validateRequired(tracker, property, ['name', 'summary', 'type'])
Expand All @@ -332,7 +384,7 @@ def validateProperty(typeTracker, property):
tracker.trackError("Constant should have 'read-only' permission.")

def validateEvent(typeTracker, event):
tracker = ErrorTracker(event['name'], typeTracker)
tracker = ErrorTracker(event['name'], ErrorTracker.TRACKER_FOR_EVENT, typeTracker)
validateKeys(tracker, event, "event")
validateRequired(tracker, event, ['name', 'summary'])
validateCommon(tracker, event)
Expand All @@ -341,7 +393,7 @@ def validateEvent(typeTracker, event):
tracker.trackError('"properties" specified, but isn\'t a list')
return
for p in event['properties']:
pTracker = ErrorTracker(p['name'], tracker)
pTracker = ErrorTracker(p['name'], ErrorTracker.TRACKER_FOR_EVENT_PROPERTY, tracker)
validateKeys(pTracker, p, "eventprop")
validateRequired(pTracker, p, ['name', 'summary'])
validateCommon(pTracker, p)
Expand Down Expand Up @@ -370,7 +422,7 @@ def validateExcludes(tracker, excludes):

def validateType(typeDoc):
typeName = typeDoc['name']
errorTrackers[typeName] = ErrorTracker(typeName)
errorTrackers[typeName] = ErrorTracker(typeName, ErrorTracker.TRACKER_FOR_TYPE)
tracker = errorTrackers[typeName]

validateRequired(tracker, typeDoc, ['name', 'summary'])
Expand Down Expand Up @@ -406,7 +458,19 @@ def validateType(typeDoc):
validateEvent(tracker, event)


def loadTypesFromDocgen():
global typesFromDocgen
import docgen
docgen.log.level = 2 # INFO
docgen.process_yaml()
docgen.finish_partial_overrides()
typesFromDocgen = docgen.apis

def validateTDoc(tdocPath):
global typesFromDocgen
if not typesFromDocgen:
loadTypesFromDocgen()

tdocTypes = [type for type in yaml.load_all(codecs.open(tdocPath, 'r', 'utf8').read())]
if options.parseOnly:
return
Expand Down Expand Up @@ -434,7 +498,7 @@ def validateMethodRefs(typeTracker, method):
validateRef(tracker, method['returns'], 'returns')
elif type(method['returns']) == dict:
returnObj = method['returns']
rTracker = ErrorTracker(returnObj, tracker)
rTracker = ErrorTracker(returnObj, ErrorTracker.TRACKER_FOR_REF, tracker)
if 'type' in returnObj:
validateRef(rTracker, returnObj['type'], 'type')
if 'parameters' in method:
Expand Down Expand Up @@ -514,6 +578,11 @@ def main(args):

dir=None
if options.file is not None:
# NOTE: because of the introduction of inherited documentation
# fields via TIMOB-7419, using the -f option is not really that
# fast anymore because even if we're just validating one file we need
# to parse all of them in order to see the "final" set of documentation
# for a type.
print "Validating %s:" % options.file
validateTDoc(options.file)
else:
Expand Down