Sat, 20 Aug 2011 16:28:25 +0200
Added support for class attributes, class methods and static methods to the class browsers and the source documentor.
--- a/DocumentationTools/ModuleDocumentor.py Sat Aug 20 10:49:36 2011 +0200 +++ b/DocumentationTools/ModuleDocumentor.py Sat Aug 20 16:28:25 2011 +0200 @@ -18,7 +18,7 @@ from . import TemplatesListsStyleCSS from Utilities import html_uencode -from Utilities.ModuleParser import RB_SOURCE +from Utilities.ModuleParser import RB_SOURCE, Function _signal = re.compile(r""" ^@signal [ \t]+ @@ -376,7 +376,12 @@ supers = 'None' globalsList = self.__genGlobalsListSection(_class) - methList, methBodies = self.__genMethodSection(_class, className) + classMethList, classMethBodies = \ + self.__genMethodSection(_class, className, Function.Class) + methList, methBodies = \ + self.__genMethodSection(_class, className, Function.General) + staticMethList, staticMethBodies = \ + self.__genMethodSection(_class, className, Function.Static) try: clsBody = self.classTemplate.format(**{ \ @@ -385,8 +390,10 @@ 'ClassSuper': supers, 'ClassDescription': self.__formatDescription(_class.description), 'GlobalsList': globalsList, + 'ClassMethodList': classMethList, 'MethodList': methList, - 'MethodDetails': methBodies, + 'StaticMethodList': staticMethList, + 'MethodDetails': classMethBodies + methBodies + staticMethBodies, }) except TagError as e: sys.stderr.write("Error in tags of description of class {0}.\n".format( @@ -398,30 +405,33 @@ return ''.join(classes) - def __genMethodsListSection(self, names, dict, className, clsName): + def __genMethodsListSection(self, names, dict, className, clsName, + includeInit=True): """ Private method to generate the methods list section of a class. - @param names The names to appear in the list. (list of strings) - @param dict A dictionary containing all relevant information. - @param className The class name containing the names. - @param clsName The visible class name containing the names. - @return The list section. (string) + @param names names to appear in the list (list of strings) + @param dict dictionary containing all relevant information + @param className class name containing the names + @param clsName visible class name containing the names + @param includeInit flag indicating to include the __init__ method (boolean) + @return methods list section (string) """ lst = [] - try: - lst.append(self.listEntryTemplate.format(**{ \ - 'Link': "{0}.{1}".format(className, '__init__'), - 'Name': clsName, - 'Description': self.__getShortDescription(dict['__init__'].description), - 'Deprecated': self.__checkDeprecated(dict['__init__'].description) and \ - self.listEntryDeprecatedTemplate or "", - })) - self.keywords.append(("{0} (Constructor)".format(className), - "#{0}.{1}".format(className, '__init__'))) - except KeyError: - pass - + if includeInit: + try: + lst.append(self.listEntryTemplate.format(**{ \ + 'Link': "{0}.{1}".format(className, '__init__'), + 'Name': clsName, + 'Description': self.__getShortDescription(dict['__init__'].description), + 'Deprecated': self.__checkDeprecated(dict['__init__'].description) and \ + self.listEntryDeprecatedTemplate or "", + })) + self.keywords.append(("{0} (Constructor)".format(className), + "#{0}.{1}".format(className, '__init__'))) + except KeyError: + pass + for name in names: lst.append(self.listEntryTemplate.format(**{ \ 'Link': "{0}.{1}".format(className, name), @@ -434,17 +444,19 @@ "#{0}.{1}".format(className, name))) return ''.join(lst) - def __genMethodSection(self, obj, className): + def __genMethodSection(self, obj, className, filter): """ Private method to generate the method details section. - @param obj Reference to the object being formatted. - @param className Name of the class containing the method. (string) - @return The method list and method details section. (tuple of two string) + @param obj reference to the object being formatted + @param className name of the class containing the method (string) + @param filter filter value designating the method types + @return method list and method details section (tuple of two string) """ methList = [] methBodies = [] - methods = sorted(list(obj.methods.keys())) + methods = sorted([k for k in obj.methods.keys() + if obj.methods[k].modifier == filter]) if '__init__' in methods: methods.remove('__init__') try: @@ -463,13 +475,20 @@ sys.stderr.write("{0}\n".format(e)) methBody = "" methBodies.append(methBody) - + + if filter == Function.Class: + methodClassifier = " (class method)" + elif filter == Function.Static: + methodClassifier = " (static)" + else: + methodClassifier = "" for method in methods: try: methBody = self.methodTemplate.format(**{ \ 'Anchor': className, 'Class': obj.name, 'Method': obj.methods[method].name, + 'MethodClassifier': methodClassifier, 'MethodDescription': \ self.__formatDescription(obj.methods[method].description), 'Params': ', '.join(obj.methods[method].parameters[1:]), @@ -482,7 +501,8 @@ methBody = "" methBodies.append(methBody) - methList = self.__genMethodsListSection(methods, obj.methods, className, obj.name) + methList = self.__genMethodsListSection(methods, obj.methods, className, + obj.name, includeInit='__init__' in methods) if not methList: methList = self.listEntryNoneTemplate @@ -501,7 +521,8 @@ for rbModuleName in rbModulesNames: rbModule = self.module.modules[rbModuleName] globalsList = self.__genGlobalsListSection(rbModule) - methList, methBodies = self.__genMethodSection(rbModule, rbModuleName) + methList, methBodies = \ + self.__genMethodSection(rbModule, rbModuleName, Function.General) classList, classBodies = \ self.__genRbModulesClassesSection(rbModule, rbModuleName) @@ -545,7 +566,8 @@ else: supers = 'None' - methList, methBodies = self.__genMethodSection(_class, className) + methList, methBodies = \ + self.__genMethodSection(_class, className, Function.General) try: clsBody = self.rbModulesClassTemplate.format(**{ \
--- a/DocumentationTools/TemplatesListsStyle.py Sat Aug 20 10:49:36 2011 +0200 +++ b/DocumentationTools/TemplatesListsStyle.py Sat Aug 20 16:28:25 2011 +0200 @@ -62,15 +62,19 @@ {{ClassSuper}} <h3 style="background-color:{Level2HeaderBgColor};color:{Level2HeaderColor}">Class Attributes</h3> {{GlobalsList}} +<h3 style="background-color:{Level2HeaderBgColor};color:{Level2HeaderColor}">Class Methods</h3> +{{ClassMethodList}} <h3 style="background-color:{Level2HeaderBgColor};color:{Level2HeaderColor}">Methods</h3> {{MethodList}} +<h3 style="background-color:{Level2HeaderBgColor};color:{Level2HeaderColor}">Static Methods</h3> +{{StaticMethodList}} {{MethodDetails}} <div align="right"><a style="color:{LinkColor}" href="#top">Up</a></div> <hr />''' methodTemplate = \ '''<a NAME="{{Anchor}}.{{Method}}" ID="{{Anchor}}.{{Method}}"></a> -<h3 style="background-color:{Level2HeaderBgColor};color:{Level2HeaderColor}">{{Class}}.{{Method}}</h3> +<h3 style="background-color:{Level2HeaderBgColor};color:{Level2HeaderColor}">{{Class}}.{{Method}}{{MethodClassifier}}</h3> <b>{{Method}}</b>(<i>{{Params}}</i>) {{MethodDescription}}'''
--- a/DocumentationTools/TemplatesListsStyleCSS.py Sat Aug 20 10:49:36 2011 +0200 +++ b/DocumentationTools/TemplatesListsStyleCSS.py Sat Aug 20 16:28:25 2011 +0200 @@ -65,15 +65,19 @@ {ClassSuper} <h3>Class Attributes</h3> {GlobalsList} +<h3>Class Methods</h3> +{ClassMethodList} <h3>Methods</h3> {MethodList} +<h3>Static Methods</h3> +{StaticMethodList} {MethodDetails} <div align="right"><a href="#top">Up</a></div> <hr />''' methodTemplate = \ '''<a NAME="{Anchor}.{Method}" ID="{Anchor}.{Method}"></a> -<h4>{Class}.{Method}</h4> +<h4>{Class}.{Method}{MethodClassifier}</h4> <b>{Method}</b>(<i>{Params}</i>) {MethodDescription}''' @@ -177,7 +181,7 @@ </dd>''' signalsListTemplate = \ -'''<h4>Signals</h4> +'''<h3>Signals</h3> <dl> {Signals} </dl>''' @@ -189,7 +193,7 @@ </dd>''' eventsListTemplate = \ -'''<h4>Events</h4> +'''<h3>Events</h3> <dl> {Events} </dl>'''
--- a/UI/BrowserModel.py Sat Aug 20 10:49:36 2011 +0200 +++ b/UI/BrowserModel.py Sat Aug 20 16:28:25 2011 +0200 @@ -612,7 +612,8 @@ if len(cl.globals): node = BrowserClassAttributesItem( parentItem, cl.globals, - QApplication.translate("BrowserModel", "Attributes (global)")) + QApplication.translate("BrowserModel", "Class Attributes"), + True) if repopulate: self.addItem(node, self.createIndex(parentItem.row(), 0, parentItem)) @@ -659,6 +660,7 @@ @param parentItem reference to the class attributes item to be populated @param repopulate flag indicating a repopulation (boolean) """ + classAttributes = parentItem.isClassAttributes() attributes = parentItem.attributes() if not attributes: return @@ -669,7 +671,8 @@ self.beginInsertRows(self.createIndex(parentItem.row(), 0, parentItem), 0, len(keys) - 1) for key in keys: - node = BrowserClassAttributeItem(parentItem, attributes[key]) + node = BrowserClassAttributeItem(parentItem, attributes[key], + classAttributes) self._addItem(node, parentItem) if repopulate: self.endInsertRows() @@ -1328,7 +1331,13 @@ self.name = name self._functionObject = fn self._filename = filename - if self._functionObject.isPrivate(): + if self._functionObject.modifier == \ + Utilities.ClassBrowsers.ClbrBaseClasses.Function.Static: + self.icon = UI.PixmapCache.getIcon("method_static.png") + elif self._functionObject.modifier == \ + Utilities.ClassBrowsers.ClbrBaseClasses.Function.Class: + self.icon = UI.PixmapCache.getIcon("method_class.png") + elif self._functionObject.isPrivate(): self.icon = UI.PixmapCache.getIcon("method_private.png") elif self._functionObject.isProtected(): self.icon = UI.PixmapCache.getIcon("method_protected.png") @@ -1406,13 +1415,14 @@ """ Class implementing the data structure for browser class attributes items. """ - def __init__(self, parent, attributes, text): + def __init__(self, parent, attributes, text, isClass=False): """ Constructor @param parent parent item @param attributes list of attributes @param text text to be shown by this item (string) + @param isClass flag indicating class attributes (boolean) """ BrowserItem.__init__(self, parent, text) @@ -1420,7 +1430,11 @@ self._attributes = attributes.copy() self._populated = False self._lazyPopulation = True - self.icon = UI.PixmapCache.getIcon("attributes.png") + if isClass: + self.icon = UI.PixmapCache.getIcon("attributes_class.png") + else: + self.icon = UI.PixmapCache.getIcon("attributes.png") + self.__isClass = isClass def attributes(self): """ @@ -1430,6 +1444,14 @@ """ return self._attributes + def isClassAttributes(self): + """ + Public method returning the attributes type. + + @return flag indicating class attributes (boolean) + """ + return self.__isClass + def lessThan(self, other, column, order): """ Public method to check, if the item is less than the other one. @@ -1452,19 +1474,22 @@ """ Class implementing the data structure for browser class attribute items. """ - def __init__(self, parent, attribute): + def __init__(self, parent, attribute, isClass=False): """ Constructor @param parent parent item @param attribute reference to the attribute object + @param isClass flag indicating a class attribute (boolean) """ BrowserItem.__init__(self, parent, attribute.name) self.type_ = BrowserItemAttribute self._attributeObject = attribute self.__public = attribute.isPublic() - if attribute.isPrivate(): + if isClass: + self.icon = UI.PixmapCache.getIcon("attribute_class.png") + elif attribute.isPrivate(): self.icon = UI.PixmapCache.getIcon("attribute_private.png") elif attribute.isProtected(): self.icon = UI.PixmapCache.getIcon("attribute_protected.png")
--- a/Utilities/ClassBrowsers/ClbrBaseClasses.py Sat Aug 20 10:49:36 2011 +0200 +++ b/Utilities/ClassBrowsers/ClbrBaseClasses.py Sat Aug 20 16:28:25 2011 +0200 @@ -222,7 +222,12 @@ """ Class to represent a function or method. """ - def __init__(self, module, name, file, lineno, signature='', separator=','): + General = 0 + Static = 1 + Class = 2 + + def __init__(self, module, name, file, lineno, signature='', separator=',', + modifierType=General): """ Constructor @@ -232,9 +237,11 @@ @param lineno linenumber of the class definition @param signature parameterlist of the method @param separator string separating the parameters + @param modifierType type of the function """ ClbrBase.__init__(self, module, name, file, lineno) self.parameters = [e.strip() for e in signature.split(separator)] + self.modifier = modifierType class Coding(ClbrBase):
--- a/Utilities/ClassBrowsers/pyclbr.py Sat Aug 20 10:49:36 2011 +0200 +++ b/Utilities/ClassBrowsers/pyclbr.py Sat Aug 20 16:28:25 2011 +0200 @@ -52,6 +52,12 @@ \] ) +| (?P<MethodModifier> + ^ + (?P<MethodModifierIndent> [ \t]* ) + (?P<MethodModifierType> @classmethod | @staticmethod ) + ) + | (?P<Method> ^ (?P<MethodIndent> [ \t]* ) @@ -142,7 +148,8 @@ """ Class to represent a Python function. """ - def __init__(self, module, name, file, lineno, signature='', separator=','): + def __init__(self, module, name, file, lineno, signature='', separator=',', + modifierType=ClbrBaseClasses.Function.General): """ Constructor @@ -152,9 +159,10 @@ @param lineno linenumber of the class definition @param signature parameterlist of the method @param separator string separating the parameters + @param modifierType type of the function """ ClbrBaseClasses.Function.__init__(self, module, name, file, lineno, - signature, separator) + signature, separator, modifierType) VisibilityMixin.__init__(self) @@ -264,13 +272,18 @@ lineno, last_lineno_pos = 1, 0 i = 0 + modifierType = ClbrBaseClasses.Function.General + modifierIndent = -1 while True: m = _getnext(src, i) if not m: break start, i = m.span() - if m.start("Method") >= 0: + if m.start("MethodModifier") >= 0: + modifierIndent = _indent(m.group("MethodModifierIndent")) + modifierType = m.group("MethodModifierType") + elif m.start("Method") >= 0: # found a method definition or function thisindent = _indent(m.group("MethodIndent")) meth_name = m.group("MethodName") @@ -279,6 +292,15 @@ meth_sig = _commentsub('', meth_sig) lineno = lineno + src.count('\n', last_lineno_pos, start) last_lineno_pos = start + if modifierType and modifierIndent == thisindent: + if modifierType == "@staticmethod": + modifier = ClbrBaseClasses.Function.Static + elif modifierType == "@classmethod": + modifier = ClbrBaseClasses.Function.Class + else: + modifier = ClbrBaseClasses.Function.General + else: + modifier = ClbrBaseClasses.Function.General # modify indentation level for conditional defines if conditionalsstack: if thisindent > conditionalsstack[-1]: @@ -304,12 +326,12 @@ if cur_class: # it's a method/nested def f = Function(None, meth_name, - file, lineno, meth_sig) + file, lineno, meth_sig, modifierType=modifier) cur_class._addmethod(meth_name, f) else: # it's a function f = Function(module, meth_name, - file, lineno, meth_sig) + file, lineno, meth_sig, modifierType=modifier) if meth_name in dict_counts: dict_counts[meth_name] += 1 meth_name = "{0}_{1:d}".format(meth_name, dict_counts[meth_name]) @@ -317,6 +339,10 @@ dict_counts[meth_name] = 0 dict[meth_name] = f classstack.append((f, thisindent)) # Marker for nested fns + + # reset the modifier settings + modifierType = ClbrBaseClasses.Function.General + modifierIndent = -1 elif m.start("String") >= 0: pass
--- a/Utilities/ModuleParser.py Sat Aug 20 10:49:36 2011 +0200 +++ b/Utilities/ModuleParser.py Sat Aug 20 16:28:25 2011 +0200 @@ -94,6 +94,12 @@ \#\#\# ) +| (?P<MethodModifier> + ^ + (?P<MethodModifierIndent> [ \t]* ) + (?P<MethodModifierType> @classmethod | @staticmethod ) + ) + | (?P<Method> (^ [ \t]* @ (?: PyQt4 \. )? (?: QtCore \. )? (?: pyqtSignature | pyqtSlot ) [ \t]* \( @@ -479,13 +485,18 @@ i = 0 modulelevel = 1 cur_obj = self + modifierType = Function.General + modifierIndent = -1 while True: m = self._getnext(src, i) if not m: break start, i = m.span() - if m.start("Method") >= 0: + if m.start("MethodModifier") >= 0: + modifierIndent = _indent(m.group("MethodModifierIndent")) + modifierType = m.group("MethodModifierType") + elif m.start("Method") >= 0: # found a method definition or function thisindent = _indent(m.group("MethodIndent")) meth_name = m.group("MethodName") @@ -501,6 +512,15 @@ meth_pyqtSig = None lineno = lineno + src.count('\n', last_lineno_pos, start) last_lineno_pos = start + if modifierType and modifierIndent == thisindent: + if modifierType == "@staticmethod": + modifier = Function.Static + elif modifierType == "@classmethod": + modifier = Function.Class + else: + modifier = Function.General + else: + modifier = Function.General # modify indentation level for conditional defines if conditionalsstack: if thisindent > conditionalsstack[-1]: @@ -538,24 +558,28 @@ if isinstance(cur_class, Class): # it's a class method f = Function(None, meth_name, None, lineno, - meth_sig, meth_pyqtSig) + meth_sig, meth_pyqtSig, modifierType=modifier) self.__py_setVisibility(f) cur_class.addMethod(meth_name, f) break else: # it's a nested function of a module function f = Function(self.name, meth_name, self.file, lineno, - meth_sig, meth_pyqtSig) + meth_sig, meth_pyqtSig, modifierType=modifier) self.__py_setVisibility(f) self.addFunction(meth_name, f) else: # it's a module function f = Function(self.name, meth_name, self.file, lineno, - meth_sig, meth_pyqtSig) + meth_sig, meth_pyqtSig, modifierType=modifier) self.__py_setVisibility(f) self.addFunction(meth_name, f) cur_obj = f classstack.append((None, thisindent)) # Marker for nested fns + + # reset the modifier settings + modifierType = Function.General + modifierIndent = -1 elif m.start("Docstring") >= 0: contents = m.group("DocstringContents3") @@ -1216,7 +1240,12 @@ ''' Class to represent a Python function or method. ''' - def __init__(self, module, name, file, lineno, signature='', pyqtSignature=None): + General = 0 + Static = 1 + Class = 2 + + def __init__(self, module, name, file, lineno, signature='', pyqtSignature=None, + modifierType=General): """ Constructor @@ -1226,6 +1255,7 @@ @param lineno linenumber of the function definition (integer) @param signature the functions call signature (string) @param pyqtSignature the functions PyQt signature (string) + @param modifierType type of the function """ self.module = module self.name = name @@ -1235,6 +1265,7 @@ self.parameters = [e.strip() for e in signature.split(',')] self.description = "" self.pyqtSignature = pyqtSignature + self.modifier = modifierType self.setPublic() def addDescription(self, description):
--- a/changelog Sat Aug 20 10:49:36 2011 +0200 +++ b/changelog Sat Aug 20 16:28:25 2011 +0200 @@ -8,6 +8,10 @@ Editor->Filehandling page - enhancements of the cooperation functions -- added code to the cooperation functions to support IPv6 +- enhancements to the source browser + -- show class attributes, class methods and static methods with different icons +- enhancements to the source documentor + -- introduced separate sections for class methods and static methods Version 5.2-snapshot-20110724: - bug fixes