epydoc-commits Mailing List for Python API documentation generation tool (Page 26)
Brought to you by:
edloper
You can subscribe to this list here.
2006 |
Jan
|
Feb
|
Mar
|
Apr
(77) |
May
|
Jun
(6) |
Jul
(8) |
Aug
(91) |
Sep
(67) |
Oct
(4) |
Nov
|
Dec
(1) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2007 |
Jan
(17) |
Feb
(135) |
Mar
(25) |
Apr
|
May
(1) |
Jun
(1) |
Jul
(7) |
Aug
|
Sep
(62) |
Oct
(1) |
Nov
(3) |
Dec
|
2008 |
Jan
(40) |
Feb
(102) |
Mar
(5) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2009 |
Jan
|
Feb
(2) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <ed...@us...> - 2006-04-07 01:05:22
|
Revision: 1183 Author: edloper Date: 2006-04-06 18:05:18 -0700 (Thu, 06 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1183&view=rev Log Message: ----------- - Docstring fixes - Set tooltips on nodes for ValueDocs Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-07 01:04:42 UTC (rev 1182) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-07 01:05:18 UTC (rev 1183) @@ -207,7 +207,7 @@ def render(self, language='gif'): """ - Use the `dot` command to render this graph, using the output + Use the ``dot`` command to render this graph, using the output format `language`. Return the result as a string, or `None` if the rendering failed. """ @@ -408,13 +408,13 @@ def call_graph(api_docs, docindex, linker, context=None, **options): """ - @param options: - - C{dir}: rankdir for the graph. (default=LR) - - C{add_callers}: also include callers for any of the - routines in C{api_docs}. (default=False) - - C{add_callees}: also include callees for any of the - routines in C{api_docs}. (default=False) - @todo: Add an C{exclude} option? + :param options: + - `dir`: rankdir for the graph. (default=LR) + - `add_callers`: also include callers for any of the + routines in `api_docs`. (default=False) + - `add_callees`: also include callees for any of the + routines in `api_docs`. (default=False) + :todo: Add an `exclude` option? """ if docindex.callers is None: log.warning("No profiling information for call graph!") @@ -523,7 +523,7 @@ def specialize_valdoc_node(node, val_doc, context, url): """ - Update the style attributes of C{node} to reflext its type + Update the style attributes of `node` to reflext its type and context. """ dot_version = get_dot_version() @@ -537,7 +537,9 @@ node['shape'] = 'plaintext' if val_doc == context: color = SELECTED_BG else: color = MODULE_BG + node['tooltip'] = node['label'] node['html_label'] = MODULE_NODE_HTML % (color, color, node['label']) + node['width'] = node['height'] = 0 elif isinstance(val_doc, RoutineDoc): node['shape'] = 'box' @@ -545,6 +547,7 @@ node['width'] = 0 node['height'] = 0 node['label'] = '%s()' % node['label'] + node['tooltip'] = node['label'] if val_doc == context: node['fillcolor'] = SELECTED_BG node['style'] = 'filled,rounded' @@ -553,6 +556,7 @@ node['shape'] = 'box' node['width'] = 0 node['height'] = 0 + node['tooltip'] = node['label'] if val_doc == context: node['fillcolor'] = SELECTED_BG node['style'] = 'filled' This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-07 01:04:46
|
Revision: 1182 Author: edloper Date: 2006-04-06 18:04:42 -0700 (Thu, 06 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1182&view=rev Log Message: ----------- - Minor change to logging message Modified Paths: -------------- trunk/epydoc/src/epydoc/docbuilder.py Modified: trunk/epydoc/src/epydoc/docbuilder.py =================================================================== --- trunk/epydoc/src/epydoc/docbuilder.py 2006-04-07 01:04:16 UTC (rev 1181) +++ trunk/epydoc/src/epydoc/docbuilder.py 2006-04-07 01:04:42 UTC (rev 1182) @@ -736,7 +736,7 @@ # If they are incompatible, then check the precedence. elif introspect_doc.posargs != parse_doc.posargs: - log.info("Warning: Not merging the parsed & introspected arg " + log.info("Not merging the parsed & introspected arg " "lists for %s, since they don't match (%s vs %s)" % (path, introspect_doc.posargs, parse_doc.posargs)) if (MERGE_PRECEDENCE.get('posargs', DEFAULT_MERGE_PRECEDENCE) == @@ -831,7 +831,7 @@ # If the lengths don't match up, then give up. This is most # often caused by __metaclass__. if len(baselist1) != len(baselist2): - log.info("Warning: Not merging the introspected & parsed base lists " + log.info("Not merging the introspected & parsed base lists " "for %s, since their lengths don't match (%s vs %s)" % (path, len(baselist1), len(baselist2))) if precedence == 'introspect': return baselist1 @@ -842,7 +842,7 @@ if ((base1.canonical_name not in (None, UNKNOWN) and base2.canonical_name not in (None, UNKNOWN)) and base1.canonical_name != base2.canonical_name): - log.info("Warning: Not merging the parsed & introspected base " + log.info("Not merging the parsed & introspected base " "lists for %s, since the bases' names don't match " "(%s vs %s)" % (path, base1.canonical_name, base2.canonical_name)) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-07 01:04:20
|
Revision: 1181 Author: edloper Date: 2006-04-06 18:04:16 -0700 (Thu, 06 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1181&view=rev Log Message: ----------- - Added --check action - Fixed usage typos - Changed config file options a little: now 'output' is used to specify the output type (html, pdf, etc); and 'target' is used to specify the destination directory. Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2006-04-07 01:02:08 UTC (rev 1180) +++ trunk/epydoc/src/epydoc/cli.py 2006-04-07 01:04:16 UTC (rev 1181) @@ -70,6 +70,8 @@ INHERITANCE_STYLES = ('grouped', 'listed', 'included') GRAPH_TYPES = ('classtree', 'callgraph') +ACTIONS = ('html', 'text', 'latex', 'dvi', 'ps', 'pdf', 'check') +DEFAULT_DOCFORMAT = 'epytext' ###################################################################### #{ Argument & Config File Parsing @@ -104,7 +106,7 @@ help="Write PDF output.") action_group.add_option( # --check "--check", action="store_const", dest="action", const="check", - help="Check completeness of docs. (not implemented yet)") + help="Check completeness of docs.") # Add options -- Options options_group.add_option( # --output @@ -118,7 +120,7 @@ options_group.add_option( # --output "--docformat", dest="docformat", metavar="NAME", help="The default markup language for docstrings. Defaults " - "to \"%default\".") + "to \"%s\"." % DEFAULT_DOCFORMAT) options_group.add_option( # --css "--css", dest="css", metavar="STYLESHEET", help="The CSS stylesheet. STYLESHEET can be either a " @@ -193,7 +195,7 @@ "Graphs are generated using the Graphviz dot executable. " "If this executable is not on the path, then use --dotpath " "to specify its location. This option may be repeated to " - "include multiple graph types in the output. GRAPHTYPE" + "include multiple graph types in the output. GRAPHTYPE " "should be one of: all, %s." % ', '.join(GRAPH_TYPES))) options_group.add_option( '--separate-classes', action='store_true', @@ -219,7 +221,7 @@ # Set the option parser's defaults. optparser.set_defaults(action="html", show_frames=True, - docformat='epytext', + docformat=DEFAULT_DOCFORMAT, show_private=True, show_imports=False, inheritance="listed", verbose=0, quiet=0, @@ -299,12 +301,17 @@ if optname in ('modules', 'objects', 'values', 'module', 'object', 'value'): names.extend(val.replace(',', ' ').split()) - elif optname in ('output', 'target'): + elif optname == 'output': + if optname not in ACTIONS: + raise ValueError('"%s" expected one of: %s' % + (optname, ', '.join(ACTIONS))) + options.action = action + elif optname == 'target': options.target = val elif optname == 'inheritance': if val.lower() not in INHERITANCE_STYLES: - raise ValueError('"inheritance" expected one of: %s.' % - ', '.join(INHERITANCE_STYLES)) + raise ValueError('"%s" expected one of: %s.' % + (optname, ', '.join(INHERITANCE_STYLES))) options.inerhitance = val.lower() elif optname == 'docformat': options.docformat = val @@ -330,7 +337,7 @@ try: options.verbosity = int(val) except ValueError: - raise ValueError('"verbosity" expected an int') + raise ValueError('"%s" expected an int' % optname) elif optname == 'parse': options.parse = _str_to_bool(val, optname) elif optname == 'introspect': @@ -341,8 +348,8 @@ graphtypes = val.replace(',', '').split() for graphtype in graphtypes: if graphtype not in GRAPH_TYPES: - raise ValueError('"graph" expected one of: %s.' % - ', '.join(GRAPH_TYPES)) + raise ValueError('"%s" expected one of: %s.' % + (optname, ', '.join(GRAPH_TYPES))) options.graphs.extend(graphtypes) elif optname in ('separate-classes', 'separate_classes'): options.list_classes_separately = _str_to_bool(val, optname) @@ -392,6 +399,7 @@ elif options.action == 'dvi': stages += [60,30] elif options.action == 'ps': stages += [60,40] elif options.action == 'pdf': stages += [60,50] + elif options.action == 'check': stages += [10] else: raise ValueError, '%r not supported' % options.action if options.parse and not options.introspect: del stages[1] # no merging @@ -443,6 +451,8 @@ write_latex(docindex, options, options.action) elif options.action == 'text': write_text(docindex, options) + elif options.action == 'check': + check_docs(docindex, options) else: print >>sys.stderr, '\nUnsupported action %s!' % options.action @@ -564,6 +574,10 @@ if isinstance(s, unicode): s = s.encode('ascii', 'backslashreplace') print s + +def check_docs(docindex, options): + from epydoc.checker import DocChecker + DocChecker(docindex).check() def cli(): # Parse command-line arguments. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-07 01:02:12
|
Revision: 1180 Author: edloper Date: 2006-04-06 18:02:08 -0700 (Thu, 06 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1180&view=rev Log Message: ----------- - Updated to work with epydoc 3.0 Modified Paths: -------------- trunk/epydoc/src/epydoc/checker.py Modified: trunk/epydoc/src/epydoc/checker.py =================================================================== --- trunk/epydoc/src/epydoc/checker.py 2006-04-06 04:35:12 UTC (rev 1179) +++ trunk/epydoc/src/epydoc/checker.py 2006-04-07 01:02:08 UTC (rev 1180) @@ -28,26 +28,12 @@ # versions: _NO_BASIC = ['__hash__', '__repr__', '__str__', '__cmp__'] -# The following methods never need return values: -_NO_RETURN = ['__init__', '__hash__', '__repr__', '__str__', - '__cmp__'] +# The following methods never need return value descriptions. +_NO_RETURN = ['__init__', '__hash__', '__repr__', '__str__', '__cmp__'] # The following methods don't need parameters documented: _NO_PARAM = ['__cmp__'] -def _is_private(str): - """ - @return: True if C{str} is the name of a public object. - @rtype: C{boolean} - @param str: The name to check. - @type str: C{string} - """ - if str == '...': return 0 - for piece in str.split('.'): - if piece[0] == '_' and piece[-1] != '_': - return 1 - return 0 - class DocChecker: """ Documentation completeness checker. C{DocChecker} can be used to @@ -65,7 +51,7 @@ checked: L{MODULE}; L{CLASS}; L{FUNC}; L{VAR}; L{IVAR}; L{CVAR}; L{PARAM}; and L{RETURN}. - Public/private specifiers indicate whether public or private - objects should be checked: L{PUBLIC} and L{PRIVATE}. + objects should be checked: L{PRIVATE}. - Check specifiers indicate what checks should be run on the objects: L{TYPE}; L{DESCR}; L{DESCR_LAZY}; L{AUTHOR}; and L{VERSION}. @@ -74,15 +60,13 @@ documentation. Its parameter is formed by or-ing together at least one value from each specifier group: - >>> checker.check(DocChecker.MODULE | DocChecker.PUBLIC | - ... DocChecker.DESCR) + >>> checker.check(DocChecker.MODULE | DocChecker.DESCR) To specify multiple values from a single group, simply or their values together: >>> checker.check(DocChecker.MODULE | DocChecker.CLASS | - ... DocChecker.FUNC | DocChecker.DESCR_LAZY | - ... DocChecker.PUBLIC) + ... DocChecker.FUNC | DocChecker.DESCR_LAZY) @group Types: MODULE, CLASS, FUNC, VAR, IVAR, CVAR, PARAM, RETURN, ALL_T @@ -124,12 +108,6 @@ @type VERSION: C{int} @cvar VERSION: Check specifier that indicates that every object should have a C{version} field. - @type DESCR_LAZY: C{int} - @cvar DESCR_LAZY: Check specifier that indicates that every object - should have a description. However, it is permissible for - functions and methods that have a C{@return} field to not have - a description, since a description may be generated from the - C{@return} field. @type DESCR: C{int} @cvar DESCR: Check specifier that indicates that every object should have a description. @@ -137,24 +115,18 @@ @cvar ALL_C: Check specifier that indicates that all checks should be run. - @group Publicity: PUBLIC, PRIVATE, ALL_P - @type PUBLIC: C{int} - @cvar PUBLIC: Specifier that indicates that public objects should - be checked. + @group Publicity: PRIVATE @type PRIVATE: C{int} @cvar PRIVATE: Specifier that indicates that private objects should be checked. - @type ALL_P: C{int} - @cvar ALL_P: Specifier that indicates that both public and private - objects should be checked. """ # Types MODULE = 1 CLASS = 2 FUNC = 4 VAR = 8 - IVAR = 16 - CVAR = 32 + #IVAR = 16 + #CVAR = 32 PARAM = 64 RETURN = 128 PROPERTY = 256 @@ -164,17 +136,13 @@ TYPE = 256 AUTHOR = 1024 VERSION = 2048 - DESCR_LAZY = 4096 - _DESCR_STRICT = 8192 - DESCR = DESCR_LAZY + _DESCR_STRICT - ALL_C = 256+512+1024+2048+4096+8192 + DESCR = 4096 + ALL_C = 256+512+1024+2048+4096 # Private/public PRIVATE = 16384 - PUBLIC = 32768 - ALL_P = PRIVATE + PUBLIC - ALL = ALL_T + ALL_C + ALL_P + ALL = ALL_T + ALL_C + PRIVATE def __init__(self, docindex): """ @@ -186,12 +154,6 @@ @type docindex: L{Docindex<apidoc.DocIndex>} """ self._docindex = docindex - - # Sort & filter the documentation from the docindex. - docs = docindex.items() - docs.sort() - self._docs = [d for (u,d) in docs if u.is_module() or - docindex.has_key(u.module())] # Initialize instance variables self._checks = 0 @@ -199,7 +161,7 @@ self._out = sys.stdout self._num_warnings = 0 - def check(self, checks = None): + def check(self, *check_sets): """ Run the specified checks on the documentation of the objects contained by this C{DocChecker}'s C{DocIndex}. Any errors found @@ -216,18 +178,38 @@ @return: True if no problems were found. @rtype: C{boolean} """ - if checks == None: - return (self.check(DocChecker.MODULE | DocChecker.CLASS | - DocChecker.FUNC | DocChecker.DESCR_LAZY | - DocChecker.PUBLIC) and - self.check(DocChecker.PARAM | DocChecker.VAR | - DocChecker.IVAR | DocChecker.CVAR | - DocChecker.RETURN | DocChecker.DESCR | - DocChecker.TYPE | DocChecker.PUBLIC)) + if not check_sets: + check_sets = (DocChecker.MODULE | DocChecker.CLASS | + DocChecker.FUNC | DocChecker.VAR | + DocChecker.DESCR,) + + self._warnings = {} + log.start_progress('Checking docs') + for j, checks in enumerate(check_sets): + self._check(checks) + log.end_progress() + for (warning, docs) in self._warnings.items(): + docs = sorted(docs) + docnames = '\n'.join([' - %s' % self._name(d) for d in docs]) + log.warning('%s:\n%s' % (warning, docnames)) + + def _check(self, checks): self._checks = checks - self._last_warn = None - for doc in self._docs: + + # Get the list of objects to check. + valdocs = sorted(self._docindex.reachable_valdocs( + imports=False, packages=False, bases=False, submodules=False, + subclasses=False, private = (checks & DocChecker.PRIVATE))) + docs = set() + for d in valdocs: + if not isinstance(d, GenericValueDoc): docs.add(d) + for doc in valdocs: + if isinstance(doc, NamespaceDoc): + for d in doc.variables.values(): + if isinstance(d.value, GenericValueDoc): docs.add(d) + + for i, doc in enumerate(sorted(docs)): if isinstance(doc, ModuleDoc): self._check_module(doc) elif isinstance(doc, ClassDoc): @@ -236,45 +218,16 @@ self._check_func(doc) elif isinstance(doc, PropertyDoc): self._check_property(doc) + elif isinstance(doc, VariableDoc): + self._check_var(doc) else: - raise AssertionError("Don't know how to check%r" % doc) - if self._last_warn is not None: print - return (self._last_warn is None) + log.error("Don't know how to check %r" % doc) - def _warn(self, error, name): - """ - Print a warning about the object named C{name}. + def _name(self, doc): + name = str(doc.canonical_name) + if isinstance(doc, RoutineDoc): name += '()' + return name - @param error: The error message to print. - @type error: C{string} - @param name: The name of the object that generated the - warning. - @type name: C{string} - @rtype: C{None} - """ - name = str(name) - if name != self._last_warn: - self._num_warnings += 1 - if self._last_warn is not None: self._out.write('\n') - self._out.write(name + '.'*max(1,60-len(name)) + error) - self._last_warn = name - else: - self._out.write(', %s' % error) - - def _check_name_publicity(self, name): - """ - @return: True if an object named C{name} should be checked, - given the public/private specifiers. - @rtype: C{boolean} - @param name: The name of the object to check. - @type name: C{string} - """ - if (_is_private(name) and - not (self._checks & DocChecker.PRIVATE)): return 0 - if (not _is_private(name) and - not (self._checks & DocChecker.PUBLIC)): return 0 - return 1 - def _check_basic(self, doc): """ Check the description, author, version, and see-also fields of @@ -285,117 +238,115 @@ @type doc: L{ObjDoc} @rtype: C{None} """ - if (self._checks & DocChecker.DESCR) and (not doc.descr): - if ((self._checks & DocChecker._DESCR_STRICT) or - (not isinstance(doc, RoutineDoc)) or - (not doc.returns().descr)): - self._warn('No descr', doc.canonical_name) + if ((self._checks & DocChecker.DESCR) and + (doc.descr in (None, UNKNOWN))): + if doc.docstring in (None, UNKNOWN): + self.warning('Undocumented', doc) + else: + self.warning('No description', doc) if self._checks & DocChecker.AUTHOR: - for field in doc.fields(): - if 'author' in field.tags: break + for tag, arg, descr in doc.metadata: + if 'author' == tag: break else: - self._warn('No authors', doc.canonical_name) + self.warning('No authors', doc) if self._checks & DocChecker.VERSION: - for field in doc.fields(): - if 'version' in field.tags: break + for tag, arg, descr in doc.metadata: + if 'version' == tag: break else: - self._warn('No version', doc.canonical_name) + self.warning('No version', doc) def _check_module(self, doc): """ - Run checks on the module whose UID is C{doc}. + Run checks on the module whose APIDoc is C{doc}. - @param doc: The UID of the module to check. - @type doc: L{UID} + @param doc: The APIDoc of the module to check. + @type doc: L{APIDoc} @rtype: C{None} """ - if not self._check_name_publicity(doc.canonical_name): return if self._checks & DocChecker.MODULE: self._check_basic(doc) - if self._checks & DocChecker.VAR: - for v in doc.variables(): - self._check_var(v, doc.canonical_name) def _check_class(self, doc): """ - Run checks on the class whose UID is C{doc}. + Run checks on the class whose APIDoc is C{doc}. - @param doc: The UID of the class to check. - @type doc: L{UID} + @param doc: The APIDoc of the class to check. + @type doc: L{APIDoc} @rtype: C{None} """ - if not self._check_name_publicity(doc.canonical_name): return if self._checks & DocChecker.CLASS: self._check_basic(doc) - if self._checks & DocChecker.IVAR: - for v in doc.ivariables(): - self._check_var(v, doc.canonical_name) - if self._checks & DocChecker.CVAR: - for v in doc.cvariables(): - self._check_var(v, doc.canonical_name) def _check_property(self, doc): - if not self._check_name_publicity(doc.canonical_name): return if self._checks & DocChecker.PROPERTY: self._check_basic(doc) - def _check_var(self, var, name, check_type=1): + def _check_var(self, doc): """ Run checks on the variable whose documentation is C{var} and whose name is C{name}. - @param var: The documentation for the variable to check. - @type var: L{Var} - @param name: The name of the variable to check. - @type name: C{string} + @param doc: The documentation for the variable to check. + @type doc: L{Doc} @param check_type: Whether or not the variable's type should be checked. This is used to allow varargs and keyword parameters to have no type specified. @rtype: C{None} """ - if not self._check_name_publicity(name): return - if var == None: return - if not self._check_name_publicity(var.name()): return - if var.name() == 'return': - if (var.type() and - var.type().to_plaintext(None).strip().lower() == 'none'): - return - if (self._checks & DocChecker.DESCR) and (not var.descr()): - self._warn('No descr', name+'.'+var.name()) - if ((self._checks & DocChecker.TYPE) and (not var.type()) and - check_type): - self._warn('No type', name+'.'+var.name()) + if self._checks & DocChecker.VAR: + if (self._checks & (DocChecker.DESCR|DocChecker.TYPE) and + doc.descr in (None, UNKNOWN) and + doc.type_descr in (None, UNKNOWN) and + doc.docstring in (None, UNKNOWN)): + self.warning('Undocumented', doc) + else: + if (self._checks & DocChecker.DESCR and + doc.descr in (None, UNKNOWN)): + self.warning('No description', doc) + if (self._checks & DocChecker.TYPE and + doc.type_descr in (None, UNKNOWN)): + self.warning('No type information', doc) def _check_func(self, doc): """ - Run checks on the function whose UID is C{doc}. + Run checks on the function whose APIDoc is C{doc}. - @param doc: The UID of the function to check. - @type doc: L{UID} + @param doc: The APIDoc of the function to check. + @type doc: L{APIDoc} @rtype: C{None} """ name = doc.canonical_name - if not self._check_name_publicity(name): return - if (doc.uid().is_any_method() and - doc != self._docindex.documented_ancestor(doc.uid())): return if (self._checks & DocChecker.FUNC and - not doc.has_docstring() and - doc.uid().shortname() not in _NO_DOCS): - self._warn('No docs', name) + doc.docstring in (None, UNKNOWN) and + doc.canonical_name[-1] not in _NO_DOCS): + self.warning('Undocumented', doc) return if (self._checks & DocChecker.FUNC and - doc.uid().shortname() not in _NO_BASIC): + doc.canonical_name[-1] not in _NO_BASIC): self._check_basic(doc) if (self._checks & DocChecker.RETURN and - doc.uid().shortname() not in _NO_RETURN): - self._check_var(doc.returns(), name) + doc.canonical_name[-1] not in _NO_RETURN): + if (doc.return_type in (None, UNKNOWN) and + doc.return_descr in (None, UNKNOWN)): + self.warning('No return descr', doc) if (self._checks & DocChecker.PARAM and - doc.uid().shortname() not in _NO_PARAM): - if doc.uid().is_method(): - for v in doc.parameters()[1:]: - self._check_var(v, name) + doc.canonical_name[-1] not in _NO_PARAM): + if doc.arg_descrs in (None, UNKNOWN): + self.warning('No argument info', doc) else: - for v in doc.parameters(): - self._check_var(v, name) - self._check_var(doc.vararg(), name, 0) - self._check_var(doc.kwarg(), name, 0) + args_with_descr = [] + for arg, descr in doc.arg_descrs: + if isinstance(arg, basestring): + args_with_descr.append(arg) + else: + args_with_descr += arg + for posarg in doc.posargs: + if (self._checks & DocChecker.DESCR and + posarg not in args_with_descr): + self.warning('Argument(s) not described', doc) + if (self._checks & DocChecker.TYPE and + posarg not in doc.arg_types): + self.warning('Argument type(s) not described', doc) + + def warning(self, msg, doc): + self._warnings.setdefault(msg,set()).add(doc) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-06 04:35:20
|
Revision: 1179 Author: edloper Date: 2006-04-05 21:35:12 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1179&view=rev Log Message: ----------- - Fixed bug with "--graph all" option. Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2006-04-06 04:34:20 UTC (rev 1178) +++ trunk/epydoc/src/epydoc/cli.py 2006-04-06 04:35:12 UTC (rev 1179) @@ -267,8 +267,10 @@ # If it's 'all', then add everything (but don't add callgraph if # we don't have any profiling info to base them on). if graph_type == 'all': - options.graphs = GRAPH_TYPES - if not options.pstat_files: option.graphs.remove('callgraph') + if options.pstat_files: + options.graphs = GRAPH_TYPES + else: + options.graphs = [g for g in GRAPH_TYPES if g != 'callgraph'] break elif graph_type not in GRAPH_TYPES: optparser.error("Invalid graph type %s." % graph_type) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-06 04:34:23
|
Revision: 1178 Author: edloper Date: 2006-04-05 21:34:20 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1178&view=rev Log Message: ----------- - Fixed bug with "--graph all" option. Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2006-04-06 04:33:06 UTC (rev 1177) +++ trunk/epydoc/src/epydoc/cli.py 2006-04-06 04:34:20 UTC (rev 1178) @@ -268,7 +268,7 @@ # we don't have any profiling info to base them on). if graph_type == 'all': options.graphs = GRAPH_TYPES - if not options.pstat_files: graph_types.remove('callgraph') + if not options.pstat_files: option.graphs.remove('callgraph') break elif graph_type not in GRAPH_TYPES: optparser.error("Invalid graph type %s." % graph_type) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-06 04:33:12
|
Revision: 1177 Author: edloper Date: 2006-04-05 21:33:06 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1177&view=rev Log Message: ----------- - PythonSourceColorizer now generates links back into the documentation for all identifiers in the source whose short name is equal to at least one documented object. If multiple documented objects have the same short name, then pop up a little box which lets the user select which item they're interested in. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/html.py trunk/epydoc/src/epydoc/docwriter/html_colorize.py trunk/epydoc/src/epydoc/docwriter/html_css.py Modified: trunk/epydoc/src/epydoc/docwriter/html.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html.py 2006-04-06 04:29:04 UTC (rev 1176) +++ trunk/epydoc/src/epydoc/docwriter/html.py 2006-04-06 04:33:06 UTC (rev 1177) @@ -652,7 +652,8 @@ self.href(doc, label='%s %s' % (self.doc_kind(doc), name))) out('<div class="py-src">\n') out('<pre class="py-src">\n') - out(PythonSourceColorizer(filename, name).colorize()) + out(PythonSourceColorizer(filename, name, self.docindex, + self.indexed_docs, self.url).colorize()) out('</pre>\n</div>\n<br />\n') # Footer Modified: trunk/epydoc/src/epydoc/docwriter/html_colorize.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html_colorize.py 2006-04-06 04:29:04 UTC (rev 1176) +++ trunk/epydoc/src/epydoc/docwriter/html_colorize.py 2006-04-06 04:33:06 UTC (rev 1177) @@ -20,6 +20,7 @@ from epydoc import log from epydoc.util import decode_with_backslashreplace, plaintext_to_html from epydoc.util import py_src_filename +from epydoc.apidoc import * ###################################################################### ## Regular expression colorizer @@ -433,8 +434,7 @@ var s = ""; if (linenumpadding != "") s = "<span class=\'lineno\'>" + linenumpadding + "</span>" - s = s + "<span class=\'pysrc-toggle\'> </span>" + - "<span class=\'py-line\'>" + indent + + s = s + " <span class=\'py-line\'>" + indent + "<a href=\'#\' onclick=\'expand(\\"" + id + "\\");return false\'>...</a></span><br />"; elt.innerHTML = s; @@ -498,6 +498,69 @@ } } +function kill_doclink() { + if (!this.contains(event.toElement)) { + var parent = document.getElementById(this.parentID); + parent.removeChild(parent.childNodes.item(0)); + } +} + +function doclink(id, name, targets) { + var elt = document.getElementById(id); + + // If we already opened the box, then destroy it. + // (This case should never occur, but leave it in just in case.) + if (elt.childNodes.length > 1) { + elt.removeChild(elt.childNodes.item(0)); + } + else { + // The outer box: relative + inline positioning. + var box1 = document.createElement("div"); + box1.style.position = "relative"; + box1.style.display = "inline"; + box1.style.top = 0; + box1.style.left = 0; + + // A shadow for fun + var shadow = document.createElement("div"); + shadow.style.position = "absolute"; + shadow.style.left = "-1.3em"; + shadow.style.top = "-1.3em"; + shadow.style.background = "#404040"; + + // The inner box: absolute positioning. + var box2 = document.createElement("div"); + box2.style.position = "relative"; + box2.style.border = "1px solid #a0a0a0"; + box2.style.left = "-.2em"; + box2.style.top = "-.2em"; + box2.style.background = "white"; + box2.style.padding = ".3em .4em .3em .4em"; + box2.style.fontStyle = "normal"; + box2.onmouseout=kill_doclink; + box2.parentID = id; + + var links = ""; + target_list = targets.split(","); + for (var i=0; i<target_list.length; i++) { + var target = target_list[i].split("="); + links += "<li><a href=\'" + target[1] + + "\' style=\'text-decoration:none\'>" + + target[0] + "</a></li>"; + } + + // Put it all together. + elt.insertBefore(box1, elt.childNodes.item(0)); + //box1.appendChild(box2); + box1.appendChild(shadow); + shadow.appendChild(box2); + box2.innerHTML = "Which <b>"+name+"</b> do you want to see "+ + "documentation for?" + + "<ul style=\'margin-bottom: 0;\'>" + + links + "</ul>"; + } +} + expandto(location.href); // --> </script> @@ -544,12 +607,7 @@ - Unicode input is supported (including automatic detection of C{'coding:'} declarations). - Still to do: - - cross-referencing within the code..? - - """ - #: A look-up table that is used to determine which CSS class #: should be used to colorize a given token. The following keys #: may be used: @@ -612,7 +670,10 @@ #: add line numbers. ADD_LINE_NUMBERS = True - def __init__(self, module_filename, module_name): + ADD_TOOLTIPS = False + + def __init__(self, module_filename, module_name, + docindex=None, api_docs=None, url_func=None): """ Create a new HTML colorizer for the specified module. @@ -632,6 +693,19 @@ #: The dotted name of the module we're colorizing. self.module_name = module_name + self.docindex = docindex + + #: A mapping from short names to lists of ValueDoc. + self.name_to_docs = {} + for api_doc in api_docs: + if (api_doc.canonical_name is not None and + url_func(api_doc) is not None): + name = api_doc.canonical_name[-1] + self.name_to_docs.setdefault(name,set()).add(api_doc) + + #: A function that maps APIDoc -> URL + self.url_func = url_func + #: The index in C{text} of the last character of the last #: token we've processed. self.pos = 0 @@ -668,6 +742,7 @@ #: on the previous logical line, or C{None} if the previous #: logical line was not a class or function definition. self.def_name = None + def find_line_offsets(self): """ @@ -741,9 +816,6 @@ html += PYSRC_JAVASCRIPTS return html - - # Add header/footer and return - #return PYSRC_TEMPLATE % (self.module_name, html) def tokeneater(self, toktype, toktext, (srow,scol), (erow,ecol), line): """ @@ -776,6 +848,8 @@ self.handle_line(self.cur_line) self.cur_line = [] + _next_uid = 0 + def handle_line(self, line): """ Render a single logical line from the module, and write the @@ -797,18 +871,21 @@ ended_def_blocks = 0 # The html output. - s = '<span class="pysrc-toggle"> </span>' if self.ADD_LINE_NUMBERS: - s = self.lineno_to_html() + s + s = self.lineno_to_html() self.lineno += 1 - s += '<span class="py-line">' + else: + s = '' + s += ' <span class="py-line">' # Loop through each token, and colorize it appropriately. for i, (toktype, toktext) in enumerate(line): # For each token, determine its css class and whether it - # should link to an href. + # should link to a url. css_class = None - href = None + url = None + tooltip = None + onclick = uid = None # these 3 are used together. # Is this token the class name in a class definition? If # so, then make it a link back into the API docs. @@ -818,7 +895,7 @@ def_name = toktext if None not in self.context: cls_name = '.'.join(self.context+[def_name]) - href = self.name2href(cls_name) + url = self.name2url(cls_name) s = self.mark_def(s, cls_name) # Is this token the function name in a function def? If @@ -830,7 +907,7 @@ if None not in self.context: cls_name = '.'.join(self.context) func_name = '.'.join(self.context+[def_name]) - href = self.name2href(cls_name, def_name) + url = self.name2url(cls_name, def_name) s = self.mark_def(s, func_name) # For each indent, update the indents list (which we use @@ -882,8 +959,32 @@ ((i>0 and line[i-1][1]=='@') or (i>1 and line[i-1][0]==None and line[i-2][1] == '@'))): css_class = self.CSS_CLASSES['DECORATOR'] - + # If it's a name, try to link it. + elif toktype == token.NAME: + css_class = self.CSS_CLASSES['NAME'] + # If we have a variable named `toktext` in the current + # context, then link to that. Note that if we're inside + # a function, then that function is our context, not + # the namespace that contains it. [xx] this isn't always + # the right thing to do. + if None not in self.context: + container = DottedName(self.module_name, *self.context) + doc = self.docindex.get_vardoc(container+toktext) + if doc is not None: + url = self.url_func(doc) + # Otherwise, check the name_to_docs index to see what + # else this name might refer to. + if url is None: + docs = sorted(self.name_to_docs.get(toktext, [])) + if docs: + if len(docs) == 1: + url = self.url_func(docs[0]) + tooltip='%s' % docs[0].canonical_name + else: + uid, onclick = self.doclink(toktext, docs) + tooltip='*.%s' % toktext + # For all other tokens, look up the CSS class to use # based on the token's type. else: @@ -905,20 +1006,32 @@ if toktext in (')',']','}'): in_param_default -= 1 if toktext == ',' and in_param_default == 1: in_param_default = 0 - # Write this token, with appropriate colorization. - if href: s += '<a href="%s" class="%s">' % (href, css_class) - elif css_class: s += '<span class="%s">' % css_class + if tooltip and self.ADD_TOOLTIPS: + tooltip_html = ' title="%s"' % tooltip + else: tooltip_html = '' + if css_class: css_class_html = ' class="%s"' % css_class + else: css_class_html = '' + if onclick: + s += ('<span id="%s"%s><a%s%s href="#" onclick="%s">' % + (uid, css_class_html, tooltip_html, + css_class_html, onclick)) + elif url: + s += ('<a%s%s href="%s">' % + (tooltip_html, css_class_html, url)) + elif css_class_html or tooltip_html: + s += '<span%s%s>' % (tooltip_html, css_class_html) if i == len(line)-1: s += ' </span>' # Closes <span class="py-line"> s += cgi.escape(toktext) else: s += self.add_line_numbers(cgi.escape(toktext), css_class) - - if href: s += '</a>' - elif css_class: s += '</span>' + if onclick: s += "</a></span>" + if url: s += '</a>' + elif css_class_html or tooltip_html: s += '</span>' + if self.ADD_DEF_BLOCKS: for i in range(ended_def_blocks): self.out(self.END_DEF_BLOCK) @@ -929,7 +1042,7 @@ if def_name and None not in self.context: self.out('</div>') - # Add divs if we're starting a def block. + # Add div's if we're starting a def block. if (self.ADD_DEF_BLOCKS and def_name and (line[-2][1] == ':') and None not in self.context): indentation = (''.join(self.indents)+' ').replace(' ', ' ') @@ -940,13 +1053,56 @@ self.def_name = def_name + def doclink(self, name, docs): + uid = 'link-%s' % self._next_uid + self._next_uid += 1 + if None not in self.context: + container = DottedName(self.module_name, *self.context) + else: + container = None + targets = ['%s=%s' % (self.doc_descr(d,container), self.url_func(d)) + for d in docs] + onclick = ("doclink('%s', '%s', '%s'); return false;" % + (uid, name, ','.join(targets))) + return uid, onclick + + def doc_descr(self, doc, context): + name = doc.canonical_name.contextualize(context) + descr = '%s %s' % (self.doc_kind(doc), name) + if isinstance(doc, RoutineDoc): + descr += '()' + return descr + + # [XX] copied streight from html.py; this should be consolidated, + # probably into apidoc. + def doc_kind(self, doc): + if isinstance(doc, ModuleDoc) and doc.is_package == True: + return 'Package' + elif (isinstance(doc, ModuleDoc) and + doc.canonical_name[0].startswith('script')): + return 'Script' + elif isinstance(doc, ModuleDoc): + return 'Module' + elif isinstance(doc, ClassDoc): + return 'Class' + elif isinstance(doc, ClassMethodDoc): + return 'Class Method' + elif isinstance(doc, StaticMethodDoc): + return 'Static Method' + elif isinstance(doc, RoutineDoc): + if isinstance(self.docindex.container(doc), ClassDoc): + return 'Method' + else: + return 'Function' + else: + return 'Variable' + def mark_def(self, s, name): replacement = ('<a name="%s"></a><div id="%s-def">\\1' '<a class="pysrc-toggle" href="#" id="%s-toggle" ' 'onclick="toggle(\'%s\'); return false;">-</a>\\2' % (name, name, name, name)) - return re.sub('(.*<span class="pysrc-toggle">) (</span>.*)\Z', - replacement, s) + return re.sub('(.*) (<span class="py-line">.*)\Z', replacement, s) def is_docstring(self, line, i): if line[i][0] != token.STRING: return False @@ -967,8 +1123,7 @@ result += '\n' if self.ADD_LINE_NUMBERS: result += self.lineno_to_html() - result += '<span class="pysrc-toggle"> </span>' - result += '<span class="py-line">' + result += ' <span class="py-line">' if css_class: result += '<span class="%s">' % css_class start = end end = s.find('\n', end)+1 @@ -976,7 +1131,7 @@ result += s[start:] return result - def name2href(self, class_name, func_name=None): + def name2url(self, class_name, func_name=None): if class_name: class_name = '%s.%s' % (self.module_name, class_name) if func_name: Modified: trunk/epydoc/src/epydoc/docwriter/html_css.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html_css.py 2006-04-06 04:29:04 UTC (rev 1176) +++ trunk/epydoc/src/epydoc/docwriter/html_css.py 2006-04-06 04:33:06 UTC (rev 1177) @@ -119,8 +119,7 @@ .continue { border-top: 0; } /* Links */ -a.navbar:link { text-decoration: none; } -a.navbar:visited { text-decoration: none; } +a.navbar { text-decoration: none; } /* Source Code Listings */ pre.py-src { border: 2px solid black; } @@ -128,12 +127,11 @@ border-bottom: 1px solid black; } div.highlight { border-bottom: 2px solid black; } a.pysrc-toggle { text-decoration: none; } -span.pysrc-toggle { font-size: 80%; font-style: normal; - padding-left: .4em; } .py-line { border-left: 2px solid black; margin-left: .2em; padding-left: .4em; } .lineno { font-style: italic; font-size: 90%; padding-left: .5em; } +a.py-name { text-decoration: none; } /* For Graphs */ .graph-without-title { border: none; } @@ -157,7 +155,6 @@ h2 span.codelink { font-size: 58%; font-weight: normal; } span.codelink { font-size: 85%; font-weight; normal; } """ -#(* (/ 1. 1.2) .7) # Black on white, with blue highlights. This is similar to how # javadoc looks. @@ -202,6 +199,8 @@ .py-keyword { background: transparent; color: #600000; } .py-output { background: transparent; color: #404040; } .py-name { background: transparent; color: #000050; } +.py-name:link { background: transparent; color: #000050; } +.py-name:visited { background: transparent; color: #000050; } .py-number { background: transparent; color: #005000; } .py-def-name { background: transparent; color: #000060; font-weight: bold; } @@ -297,6 +296,8 @@ .py-keyword { background: transparent; color: #600000; } .py-output { background: transparent; color: #404040; } .py-name { background: transparent; color: #000050; } +.py-name:link { background: transparent; color: #000050; } +.py-name:visited { background: transparent; color: #000050; } .py-number { background: transparent; color: #005000; } .py-def-name { background: transparent; color: #000060; font-weight: bold; } @@ -391,6 +392,8 @@ .py-keyword { background: transparent; color: #800000; } .py-output { background: transparent; color: #484848; } .py-name { background: transparent; color: #000050; } +.py-name:link { background: transparent; color: #000050; } +.py-name:visited { background: transparent; color: #000050; } .py-number { background: transparent; color: #005000; } .py-def-name { background: transparent; color: #000060; font-weight: bold; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-06 04:29:08
|
Revision: 1176 Author: edloper Date: 2006-04-05 21:29:04 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1176&view=rev Log Message: ----------- - Minor changes to profiling info reader function (so that read_profiling_info) can be called multiple times) Modified Paths: -------------- trunk/epydoc/src/epydoc/apidoc.py Modified: trunk/epydoc/src/epydoc/apidoc.py =================================================================== --- trunk/epydoc/src/epydoc/apidoc.py 2006-04-05 22:05:32 UTC (rev 1175) +++ trunk/epydoc/src/epydoc/apidoc.py 2006-04-06 04:29:04 UTC (rev 1176) @@ -1468,6 +1468,8 @@ L{read_profiling_info()}. @type: C{list} of L{RoutineDoc}""" + self._funcid_to_doc = {} + #//////////////////////////////////////////////////////////// # Lookup methods #//////////////////////////////////////////////////////////// @@ -1649,25 +1651,24 @@ # The Stat object encodes functions using `funcid`s, or # tuples of (filename, lineno, funcname). Create a mapping # from these `funcid`s to `RoutineDoc`s. - funcid_to_doc = self._get_funcid_to_doc_mapping(profile_stats) + self._update_funcid_to_doc(profile_stats) for callee, (cc, nc, tt, ct, callers) in profile_stats.stats.items(): - callee = funcid_to_doc.get(callee) + callee = self._funcid_to_doc.get(callee) if callee is None: continue for caller in callers: - caller = funcid_to_doc.get(caller) + caller = self._funcid_to_doc.get(caller) if caller is None: continue self.callers.setdefault(callee, []).append(caller) self.callees.setdefault(caller, []).append(callee) - def _get_funcid_to_doc_mapping(self, profile_stats): + def _update_funcid_to_doc(self, profile_stats): """ - Return a dictionary mapping from C{pstat.Stat} funciton ids to + Update the dictionary mapping from C{pstat.Stat} funciton ids to C{RoutineDoc}s. C{pstat.Stat} function ids are tuples of C{(filename, lineno, funcname)}. """ # Maps (filename, lineno, funcname) -> RoutineDoc - funcid_to_doc = {} for val_doc in self.reachable_valdocs(): # We only care about routines. if not isinstance(val_doc, RoutineDoc): continue @@ -1681,10 +1682,8 @@ # Look up the stat_func_id funcid = (filename, val_doc.lineno, val_doc.canonical_name[-1]) if funcid in profile_stats.stats: - funcid_to_doc[funcid] = val_doc + self._funcid_to_doc[funcid] = val_doc - return funcid_to_doc - ###################################################################### ## Pretty Printing ###################################################################### This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 22:05:37
|
Revision: 1175 Author: edloper Date: 2006-04-05 15:05:32 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1175&view=rev Log Message: ----------- - Added get_dot_version function, which tries to determine the available version of dot (so we can decide which features are safeto use) - Added specialize_valdoc_node(), which takes care of displaying different subclasses of APIDoc with different styles. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-05 22:02:43 UTC (rev 1174) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-05 22:05:32 UTC (rev 1175) @@ -24,7 +24,7 @@ import sys from epydoc import log from epydoc.apidoc import * -from epydoc.util import plaintext_to_html, run_subprocess +from epydoc.util import * from epydoc.compat import * # Backwards compatibility ###################################################################### @@ -219,7 +219,7 @@ result = result.decode('utf-8') except OSError, e: log.warning("Unable to render Graphviz dot graph:\n%s" % e) - log.debug(self.to_dotfile()) + #log.debug(self.to_dotfile()) return None return result @@ -249,19 +249,34 @@ class DotGraphNode: _next_id = 0 - def __init__(self, label=None, **attribs): + def __init__(self, label=None, html_label=None, **attribs): + if label is not None and html_label is not None: + raise ValueError('Use label or html_label, not both.') if label is not None: attribs['label'] = label - self.attribs = attribs + self._html_label = html_label + self._attribs = attribs self.id = self.__class__._next_id self.__class__._next_id += 1 + def __getitem__(self, attr): + return self._attribs[attr] + + def __setitem__(self, attr, val): + if attr == 'html_label': + self._attribs.pop('label') + self._html_label = val + else: + if attr == 'label': self._html_label = None + self._attribs[attr] = val + def to_dotfile(self): """ Return the dot commands that should be used to render this node. """ - attribs = ','.join(['%s="%s"' % (k,v) for (k,v) - in self.attribs.items()]) - if attribs: attribs = ' [%s]' % attribs + attribs = ['%s="%s"' % (k,v) for (k,v) in self._attribs.items()] + if self._html_label: + attribs.insert(0, 'label=<%s>' % self._html_label) + if attribs: attribs = ' [%s]' % (','.join(attribs)) return 'node%d%s' % (self.id, attribs) class DotGraphEdge: @@ -273,14 +288,20 @@ if label is not None: attribs['label'] = label self.start = start #: :type: `DotGraphNode` self.end = end #: :type: `DotGraphNode` - self.attribs = attribs + self._attribs = attribs + def __getitem__(self, attr): + return self._attribs[attr] + + def __setitem__(self, attr, val): + self._attribs[attr] = val + def to_dotfile(self): """ Return the dot commands that should be used to render this edge. """ attribs = ','.join(['%s="%s"' % (k,v) for (k,v) - in self.attribs.items()]) + in self._attribs.items()]) if attribs: attribs = ' [%s]' % attribs return 'node%d -> node%d%s' % (self.start.id, self.end.id, attribs) @@ -294,7 +315,6 @@ hierarchies for the given packages. """ graph = DotGraph('Package Tree for %s' % name_list(packages, context), - node_defaults={'shape':'box', 'width': 0, 'height': 0}, edge_defaults={'sametail':True}) # Options @@ -362,8 +382,7 @@ return graph def import_graph(modules, docindex, linker, context=None, **options): - graph = DotGraph('Import Graph', - node_defaults={'shape':'box', 'width': 0, 'height': 0}) + graph = DotGraph('Import Graph') # Options if options.get('dir', 'RL') != 'TB': # default: right-to-left. @@ -456,6 +475,28 @@ return graph ###################################################################### +#{ Dot Version +###################################################################### + +_dot_version = None +_DOT_VERSION_RE = re.compile(r'dot version ([\d\.]+)') +def get_dot_version(): + global _dot_version + if _dot_version is None: + try: + out, err = run_subprocess([DOT_COMMAND, '-V']) + version_info = err or out + m = _DOT_VERSION_RE.match(version_info) + if m: + _dot_version = [int(x) for x in m.group(1).split('.')] + else: + _dot_version = (0,) + except RunSubprocessError, e: + _dot_version = (0,) + log.info('Detected dot version %s' % _dot_version) + return _dot_version + +###################################################################### #{ Helper Functions ###################################################################### @@ -467,17 +508,55 @@ val_docs = sorted(val_docs, key=lambda d:d.canonical_name) for i, val_doc in enumerate(val_docs): label = val_doc.canonical_name.contextualize(context.canonical_name) - if isinstance(val_doc, RoutineDoc): label = '%s()' % label node = nodes[val_doc] = DotGraphNode(label) graph.nodes.append(node) - if val_doc == context: - node.attribs.update({'fillcolor':'black', 'fontcolor':'white', - 'style':'filled'}) - else: - url = linker.url_for(val_doc) - if url: node.attribs['href'] = url + specialize_valdoc_node(node, val_doc, context, linker.url_for(val_doc)) return nodes +MODULE_NODE_HTML = ''' + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="0"> + <TR><TD BORDER="0"></TD><TD BORDER="0"></TD> + <TD HEIGHT="8" BGCOLOR="%s"></TD></TR> + <TR><TD COLSPAN="3" BGCOLOR="%s">%s</TD></TR></TABLE>'''.strip() +MODULE_BG = '#d0e0ff' +SELECTED_BG = '#ffd0d0' + +def specialize_valdoc_node(node, val_doc, context, url): + """ + Update the style attributes of C{node} to reflext its type + and context. + """ + dot_version = get_dot_version() + if val_doc != context: + if url: node['href'] = url + + if isinstance(val_doc, VariableDoc) and val_doc.value is not UNKNOWN: + val_doc = val_doc.value + + if isinstance(val_doc, ModuleDoc) and dot_version > [2]: + node['shape'] = 'plaintext' + if val_doc == context: color = SELECTED_BG + else: color = MODULE_BG + node['html_label'] = MODULE_NODE_HTML % (color, color, node['label']) + + elif isinstance(val_doc, RoutineDoc): + node['shape'] = 'box' + node['style'] = 'rounded' + node['width'] = 0 + node['height'] = 0 + node['label'] = '%s()' % node['label'] + if val_doc == context: + node['fillcolor'] = SELECTED_BG + node['style'] = 'filled,rounded' + + else: + node['shape'] = 'box' + node['width'] = 0 + node['height'] = 0 + if val_doc == context: + node['fillcolor'] = SELECTED_BG + node['style'] = 'filled' + def name_list(api_docs, context=None): if context is not None: context = context.canonical_name This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 22:02:50
|
Revision: 1174 Author: edloper Date: 2006-04-05 15:02:43 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1174&view=rev Log Message: ----------- - Added pstat option to config file Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2006-04-05 19:47:34 UTC (rev 1173) +++ trunk/epydoc/src/epydoc/cli.py 2006-04-05 22:02:43 UTC (rev 1174) @@ -333,8 +333,6 @@ options.parse = _str_to_bool(val, optname) elif optname == 'introspect': options.introspect = _str_to_bool(val, optname) - elif optname == 'profile': - options.profile = _str_to_bool(val, optname) elif optname == 'dotpath': options.dotpath = val elif optname == 'graph': @@ -348,6 +346,8 @@ options.list_classes_separately = _str_to_bool(val, optname) elif optname == 'sourcecode': options.include_source_code = _str_to_bool(val, optname) + elif optname == 'pstat': + options.pstat_files.extend(val.replace(',', ' ').split()) else: raise ValueError('Unknown option %s' % optname) @@ -593,7 +593,7 @@ except SystemExit: pass prof.dump_stats('profile.out') - + return # Use the pstats statistical browser. This is made unnecessarily # difficult because the whole browser is wrapped in an # if __name__=='__main__' clause. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 19:47:42
|
Revision: 1173 Author: edloper Date: 2006-04-05 12:47:34 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1173&view=rev Log Message: ----------- - Better unicode support for dotgraphs (encode on write, and decode cmapx files on read) Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-05 17:19:53 UTC (rev 1172) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-05 19:47:34 UTC (rev 1173) @@ -214,8 +214,12 @@ try: result, err = run_subprocess([DOT_COMMAND, '-T%s' % language], self.to_dotfile()) + # Decode into unicode, if necessary. + if language == 'cmapx' and result is not None: + result = result.decode('utf-8') except OSError, e: log.warning("Unable to render Graphviz dot graph:\n%s" % e) + log.debug(self.to_dotfile()) return None return result @@ -239,8 +243,10 @@ for edge in self.edges: lines.append(edge.to_dotfile()) lines.append('}') - return '\n'.join(lines) + # Default dot input encoding is UTF-8 + return u'\n'.join(lines).encode('utf-8') + class DotGraphNode: _next_id = 0 def __init__(self, label=None, **attribs): @@ -357,8 +363,7 @@ def import_graph(modules, docindex, linker, context=None, **options): graph = DotGraph('Import Graph', - node_defaults={'shape':'box', 'width': 0, 'height': 0}, - edge_defaults={'sametail':True}) + node_defaults={'shape':'box', 'width': 0, 'height': 0}) # Options if options.get('dir', 'RL') != 'TB': # default: right-to-left. @@ -429,8 +434,7 @@ func_set.update(docindex.callees.get(func_doc, ())) graph = DotGraph('Call Graph for %s' % name_list(api_docs, context), - node_defaults={'shape':'box', 'width': 0, 'height': 0}, - edge_defaults={'sametail':True}) + node_defaults={'shape':'box', 'width': 0, 'height': 0}) # Options if options.get('dir', 'LR') != 'TB': # default: left-to-right This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 17:19:58
|
Revision: 1172 Author: edloper Date: 2006-04-05 10:19:53 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1172&view=rev Log Message: ----------- - Better verification for --graph option values Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2006-04-05 17:12:13 UTC (rev 1171) +++ trunk/epydoc/src/epydoc/cli.py 2006-04-05 17:19:53 UTC (rev 1172) @@ -257,9 +257,18 @@ if options.action == 'text' and len(names) > 1: optparser.error("--text option takes only one name.") + # Check the list of requested graph types to make sure they're + # acceptable. + options.graphs = [graph_type.lower() for graph_type in options.graphs] for graph_type in options.graphs: - if graph_type.lower() == 'all': + if graph_type == 'callgraph' and not options.pstat_files: + optparser.error('"callgraph" graph type may only be used if ' + 'one or more pstat files are specified.') + # If it's 'all', then add everything (but don't add callgraph if + # we don't have any profiling info to base them on). + if graph_type == 'all': options.graphs = GRAPH_TYPES + if not options.pstat_files: graph_types.remove('callgraph') break elif graph_type not in GRAPH_TYPES: optparser.error("Invalid graph type %s." % graph_type) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 17:12:22
|
Revision: 1171 Author: edloper Date: 2006-04-05 10:12:13 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1171&view=rev Log Message: ----------- - for callgraphs, if no doc is specified, then use the context. Modified Paths: -------------- trunk/epydoc/src/epydoc/markup/epytext.py Modified: trunk/epydoc/src/epydoc/markup/epytext.py =================================================================== --- trunk/epydoc/src/epydoc/markup/epytext.py 2006-04-05 17:11:38 UTC (rev 1170) +++ trunk/epydoc/src/epydoc/markup/epytext.py 2006-04-05 17:12:13 UTC (rev 1171) @@ -1873,8 +1873,11 @@ return import_graph(modules, docindex, linker, context) elif graph_type == 'callgraph': - docs = [docindex.find(name, context) for name in graph_args] - docs = [doc for doc in docs if doc is not None] + if graph_args: + docs = [docindex.find(name, context) for name in graph_args] + docs = [doc for doc in docs if doc is not None] + else: + docs = [context] return call_graph(docs, docindex, linker, context) else: log.warning("Unknown graph type %s" % graph_type) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 17:11:42
|
Revision: 1170 Author: edloper Date: 2006-04-05 10:11:38 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1170&view=rev Log Message: ----------- - Added a callgraph directive to restructuredtext markup parser Modified Paths: -------------- trunk/epydoc/src/epydoc/markup/restructuredtext.py Modified: trunk/epydoc/src/epydoc/markup/restructuredtext.py =================================================================== --- trunk/epydoc/src/epydoc/markup/restructuredtext.py 2006-04-05 16:58:42 UTC (rev 1169) +++ trunk/epydoc/src/epydoc/markup/restructuredtext.py 2006-04-05 17:11:38 UTC (rev 1170) @@ -539,6 +539,7 @@ ###################################################################### #{ Graph Generation Directives ###################################################################### +# See http://docutils.sourceforge.net/docs/howto/rst-directives.html class dotgraph(docutils.nodes.image): """ @@ -593,7 +594,7 @@ else: title = '' return dotgraph(_construct_digraph, title, options.get('caption'), '\n'.join(content)) -digraph_directive.arguments = (0, 1, 1) +digraph_directive.arguments = (0, 1, True) digraph_directive.options = {'caption': directives.unchanged} digraph_directive.content = True directives.register_directive('digraph', digraph_directive) @@ -689,3 +690,24 @@ """Graph generator for L{importgraph_directive}""" modules = [d for d in docindex.root if isinstance(d, ModuleDoc)] return import_graph(modules, docindex, linker, context, **options) + +def callgraph_directive(name, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + return dotgraph(_construct_callgraph, arguments, options) +callgraph_directive.arguments = (0, 1, True) +callgraph_directive.options = {'dir': _dir_option, + 'add_callers': directives.flag, + 'add_callees': directives.flag} +callgraph_directive.content = False +directives.register_directive('callgraph', callgraph_directive) + +def _construct_callgraph(docindex, context, linker, arguments, options): + """Graph generator for L{callgraph_directive}""" + if len(arguments) == 1: + docs = [docindex.find(name, context) for name in + arguments[0].replace(',',' ').split()] + docs = [doc for doc in docs if doc is not None] + else: + docs = [context] + return call_graph(docs, docindex, linker, context, **options) + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 16:58:50
|
Revision: 1169 Author: edloper Date: 2006-04-05 09:58:42 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1169&view=rev Log Message: ----------- - Added new graph type: callgraph. - Added -pstat option, which reads profiling information from pstat file(s) and stores it in the DocIndex - Renamed --profile to --profile-epydoc, and disabled it when epydoc.DEBUG=false (to avoid confusion with -pstat) - In cli._profile, explicilty specify the globals to use. - dotgraph.add_valdoc_nodes now adds () to the labels of routines - Fixed bug in HTMLWriter.pysrc_url Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py trunk/epydoc/src/epydoc/docwriter/dotgraph.py trunk/epydoc/src/epydoc/docwriter/html.py trunk/epydoc/src/epydoc/docwriter/html_css.py trunk/epydoc/src/epydoc/markup/epytext.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2006-04-05 16:52:56 UTC (rev 1168) +++ trunk/epydoc/src/epydoc/cli.py 2006-04-05 16:58:42 UTC (rev 1169) @@ -59,7 +59,7 @@ """ __docformat__ = 'epytext en' -import sys, os, time, re +import sys, os, time, re, pstats from glob import glob from optparse import OptionParser, OptionGroup import epydoc @@ -69,7 +69,7 @@ import ConfigParser INHERITANCE_STYLES = ('grouped', 'listed', 'included') -GRAPH_TYPES = ('classtree',) +GRAPH_TYPES = ('classtree', 'callgraph') ###################################################################### #{ Argument & Config File Parsing @@ -175,10 +175,12 @@ options_group.add_option( # --introspect-only "--introspect-only", action="store_false", dest="parse", help="Get all information from introspecting (don't parse)") + if epydoc.DEBUG: + # this option is for developers, not users. + options_group.add_option( + "--profile-epydoc", action="store_true", dest="profile", + help="Run the profiler. Output will be written to profile.out") options_group.add_option( - "--profile", action="store_true", dest="profile", - help="Run the profiler. Output will be written to profile.out") - options_group.add_option( "--dotpath", dest="dotpath", metavar='PATH', help="The path to the Graphviz 'dot' executable.") options_group.add_option( @@ -207,6 +209,9 @@ '--no-sourcecode', action='store_false', dest='include_source_code', help=("Do not include source code with syntax highlighting in the " "HTML output.")) + options_group.add_option( + '--pstat', action='append', dest='pstat_files', metavar='FILE', + help="A pstat output file, to be used in generating call graphs.") # Add the option groups. optparser.add_option_group(action_group) @@ -221,7 +226,7 @@ parse=True, introspect=True, debug=epydoc.DEBUG, profile=False, graphs=[], list_classes_separately=False, - include_source_code=True) + include_source_code=True, pstat_files=[]) # Parse the arguments. options, names = optparser.parse_args() @@ -407,6 +412,19 @@ if docindex is None: return # docbuilder already logged an error. + # Load profile information, if it was given. + if options.pstat_files: + try: + profile_stats = pstats.Stats(options.pstat_files[0]) + for filename in options.pstat_files[1:]: + profile_stats.add(filename) + except KeyboardInterrupt: raise + except Exception, e: + log.error("Error reading pstat file: %s" % e) + profile_stats = None + if profile_stats is not None: + docindex.read_profiling_info(profile_stats) + # Perform the specified action. if options.action == 'html': write_html(docindex, options) @@ -559,8 +577,13 @@ print >>sys.stderr, 'Use --debug to see trace information.' def _profile(): - import profile, pstats, code - profile.run('main(*parse_arguments())', 'profile.out') + import profile, pstats + try: + prof = profile.Profile() + prof = prof.runctx('main(*parse_arguments())', globals(), {}) + except SystemExit: + pass + prof.dump_stats('profile.out') # Use the pstats statistical browser. This is made unnecessarily # difficult because the whole browser is wrapped in an Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-05 16:52:56 UTC (rev 1168) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-05 16:58:42 UTC (rev 1169) @@ -382,15 +382,88 @@ return graph +def call_graph(api_docs, docindex, linker, context=None, **options): + """ + @param options: + - C{dir}: rankdir for the graph. (default=LR) + - C{add_callers}: also include callers for any of the + routines in C{api_docs}. (default=False) + - C{add_callees}: also include callees for any of the + routines in C{api_docs}. (default=False) + @todo: Add an C{exclude} option? + """ + if docindex.callers is None: + log.warning("No profiling information for call graph!") + return DotGraph('Call Graph') # return None instead? + + if isinstance(context, VariableDoc): + context = context.value + + # Get the set of requested functions. + functions = [] + for api_doc in api_docs: + # If it's a variable, get its value. + if isinstance(api_doc, VariableDoc): + api_doc = api_doc.value + # Add the value to the functions list. + if isinstance(api_doc, RoutineDoc): + functions.append(api_doc) + elif isinstance(api_doc, NamespaceDoc): + for vardoc in api_doc.variables.values(): + if isinstance(vardoc.value, RoutineDoc): + functions.append(vardoc.value) + + # Filter out functions with no callers/callees? + # [xx] this isnt' quite right, esp if add_callers or add_callees + # options are fales. + functions = [f for f in functions if + (f in docindex.callers) or (f in docindex.callees)] + + # Add any callers/callees of the selected functions + func_set = set(functions) + if options.get('add_callers', False) or options.get('add_callees', False): + for func_doc in functions: + if options.get('add_callers', False): + func_set.update(docindex.callers.get(func_doc, ())) + if options.get('add_callees', False): + func_set.update(docindex.callees.get(func_doc, ())) + + graph = DotGraph('Call Graph for %s' % name_list(api_docs, context), + node_defaults={'shape':'box', 'width': 0, 'height': 0}, + edge_defaults={'sametail':True}) + + # Options + if options.get('dir', 'LR') != 'TB': # default: left-to-right + graph.body += 'rankdir=%s\n' % options.get('dir', 'LR') + + nodes = add_valdoc_nodes(graph, func_set, linker, context) + + # Find the edges. + edges = set() + for func_doc in functions: + for caller in docindex.callers.get(func_doc, ()): + if caller in nodes: + edges.add( (nodes[caller], nodes[func_doc]) ) + for callee in docindex.callees.get(func_doc, ()): + if callee in nodes: + edges.add( (nodes[func_doc], nodes[callee]) ) + graph.edges = [DotGraphEdge(src,dst) for (src,dst) in edges] + + return graph + ###################################################################### #{ Helper Functions ###################################################################### def add_valdoc_nodes(graph, val_docs, linker, context): + """ + @todo: Use different node styles for different subclasses of APIDoc + """ nodes = {} val_docs = sorted(val_docs, key=lambda d:d.canonical_name) for i, val_doc in enumerate(val_docs): label = val_doc.canonical_name.contextualize(context.canonical_name) + if isinstance(val_doc, RoutineDoc): label = '%s()' % label node = nodes[val_doc] = DotGraphNode(label) graph.nodes.append(node) if val_doc == context: Modified: trunk/epydoc/src/epydoc/docwriter/html.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html.py 2006-04-05 16:52:56 UTC (rev 1168) +++ trunk/epydoc/src/epydoc/docwriter/html.py 2006-04-05 16:58:42 UTC (rev 1169) @@ -697,7 +697,7 @@ if 'classtree' in self._graph_types: linker = _HTMLDocstringLinker(self, doc) graph = class_tree_graph([doc], linker, doc) - self.write_graph(out, graph) + out('<center>\n%s</center>\n' % self.render_graph(graph)) # Otherwise, use ascii-art. else: @@ -1214,6 +1214,7 @@ print >> jsfile, self.GET_COOKIE_JS print >> jsfile, self.SET_FRAME_JS print >> jsfile, self.HIDE_PRIVATE_JS + print >> jsfile, self.TOGGLE_CALLGRAPH_JS jsfile.close() #: A javascript that is used to show or hide the API documentation @@ -1308,22 +1309,49 @@ } '''.strip() + TOGGLE_CALLGRAPH_JS = ''' + function toggleCallGraph(id) { + var elt = document.getElementById(id); + if (elt.style.display == "none") + elt.style.display = "block"; + else + elt.style.display = "none"; + } + '''.strip() + #//////////////////////////////////////////////////////////// #{ 2.10. Graphs #//////////////////////////////////////////////////////////// - def write_graph(self, out, graph): + def render_graph(self, graph, css='graph-without-title'): + if graph is None: return '' # Write the graph's image to a file path = os.path.join(self._directory, graph.uid) if not graph.write('%s.gif' % path, 'gif'): - return + return '' # Generate the image map. cmapx = graph.render('cmapx') or '' # Display the graph. - out('%s\n<center>\n<img src="%s.gif" alt="%s" usemap="#%s" ' - 'ismap="ismap" class="graph-without-title"/>\n</center>' % - (cmapx, graph.uid, graph.uid, graph.uid)) + uid = graph.uid + return ('%s\n<img src="%s.gif" alt="%s" usemap="#%s" ismap="ismap" ' + 'class="%s"/>\n' % (cmapx, uid, uid, uid, css)) + + def render_callgraph(self, callgraph): + graph_html = self.render_graph(callgraph, css='callgraph') + if graph_html == '': return '' + return ('<div style="display:none" id="%s-div"><center>\n' + '<table border="0" cellpadding="0" cellspacing="0">\n' + ' <tr><td>%s</td></tr>\n' + ' <tr><th>Call Graph</th></tr>\n' + '</table><br />\n</center></div>\n' % + (callgraph.uid, graph_html)) + def callgraph_link(self, callgraph): + if callgraph is None: return '' + return ('<br /><span class="codelink"><a href="#" ' + 'onclick="toggleCallGraph(\'%s-div\');return false;">' + 'call graph</a></span> ' % callgraph.uid) + #//////////////////////////////////////////////////////////// #{ 3.1. Page Header #//////////////////////////////////////////////////////////// @@ -1794,8 +1822,19 @@ for n in arg_names]) rhs = self.docstring_to_html(arg_descr, var_doc.value, 10) arg_descrs.append( (lhs, rhs) ) - self.write_function_details_entry(out, var_doc, descr, rtype, - rdescr, arg_descrs, div_class) + # Perpare the call-graph, if requested + if 'callgraph' in self._graph_types: + linker = _HTMLDocstringLinker(self, var_doc.value) + callgraph = call_graph([var_doc.value], self.docindex, + linker, var_doc, add_callers=True, + add_callees=True) + if callgraph is not None and len(callgraph.nodes) == 0: + callgraph = None + else: + callgraph = None + self.write_function_details_entry(out, var_doc, descr, callgraph, + rtype, rdescr, arg_descrs, + div_class) # Properties elif isinstance(var_doc.value, PropertyDoc): @@ -1858,7 +1897,7 @@ write_function_details_entry = compile_template( ''' - write_function_details_entry(self, out, var_doc, descr, \ + write_function_details_entry(self, out, var_doc, descr, callgraph, \ rtype, rdescr, arg_descrs, div_class) ''', # /------------------------- Template -------------------------\ @@ -1875,8 +1914,10 @@ >>> #endif </h3> </td><td align="right" valign="top" - >$self.pysrc_link(func_doc)$ </span></td> + >$self.pysrc_link(func_doc)$ </span + >$self.callgraph_link(callgraph)$</td> </table> + $self.render_callgraph(callgraph)$ $descr$ <dl><dt></dt><dd> >>> # === parameters === @@ -2758,7 +2799,7 @@ else: return None else: - module = self.docindex.defining_module + module = api_doc.defining_module if module == UNKNOWN: return None module_pysrc_url = self.pysrc_url(module) if module_pysrc_url is None: return None Modified: trunk/epydoc/src/epydoc/docwriter/html_css.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html_css.py 2006-04-05 16:52:56 UTC (rev 1168) +++ trunk/epydoc/src/epydoc/docwriter/html_css.py 2006-04-05 16:58:42 UTC (rev 1169) @@ -139,6 +139,7 @@ .graph-without-title { border: none; } .graph-with-title { border: 1px solid black; } .graph-title { font-weight: bold; } +.callgraph { border: 1px solid black; } /* Lists */ ul { margin-top: 0; } Modified: trunk/epydoc/src/epydoc/markup/epytext.py =================================================================== --- trunk/epydoc/src/epydoc/markup/epytext.py 2006-04-05 16:52:56 UTC (rev 1168) +++ trunk/epydoc/src/epydoc/markup/epytext.py 2006-04-05 16:58:42 UTC (rev 1169) @@ -1067,7 +1067,7 @@ return stack[0] -GRAPH_TYPES = ['classtree', 'packagetree', 'importgraph'] +GRAPH_TYPES = ['classtree', 'packagetree', 'importgraph', 'callgraph'] def _colorize_graph(doc, graph, token, end, errors): """ @@ -1082,7 +1082,6 @@ for child in children: graph.removeChild(child) if len(children) != 1 or not isinstance(children[0], Text): - print len(children), children[0] bad_graph_spec = "Bad graph specification" else: pieces = children[0].data.split(None, 1) @@ -1872,6 +1871,11 @@ elif graph_type == 'importgraph': modules = [d for d in docindex.root if isinstance(d, ModuleDoc)] return import_graph(modules, docindex, linker, context) + + elif graph_type == 'callgraph': + docs = [docindex.find(name, context) for name in graph_args] + docs = [doc for doc in docs if doc is not None] + return call_graph(docs, docindex, linker, context) else: log.warning("Unknown graph type %s" % graph_type) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 16:53:00
|
Revision: 1168 Author: edloper Date: 2006-04-05 09:52:56 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1168&view=rev Log Message: ----------- - Added RoutineDoc.lineno Modified Paths: -------------- trunk/epydoc/src/epydoc/docintrospecter.py trunk/epydoc/src/epydoc/docparser.py Modified: trunk/epydoc/src/epydoc/docintrospecter.py =================================================================== --- trunk/epydoc/src/epydoc/docintrospecter.py 2006-04-05 16:52:41 UTC (rev 1167) +++ trunk/epydoc/src/epydoc/docintrospecter.py 2006-04-05 16:52:56 UTC (rev 1168) @@ -443,6 +443,10 @@ routine_doc.posargs = routine_doc.posargs[1:] routine_doc.posarg_defaults = routine_doc.posarg_defaults[1:] + # Set the routine's line number. + if hasattr(func, 'func_code'): + routine_doc.lineno = func.func_code.co_firstlineno + else: # [XX] I should probably use UNKNOWN here?? routine_doc.posargs = ['...'] Modified: trunk/epydoc/src/epydoc/docparser.py =================================================================== --- trunk/epydoc/src/epydoc/docparser.py 2006-04-05 16:52:41 UTC (rev 1167) +++ trunk/epydoc/src/epydoc/docparser.py 2006-04-05 16:52:56 UTC (rev 1168) @@ -1360,7 +1360,7 @@ # Create the function's RoutineDoc. func_doc = RoutineDoc(canonical_name=canonical_name, defining_module=parent_docs[0], - docs_extracted_by='parser') + lineno=lineno, docs_extracted_by='parser') # Process the signature. init_arglist(func_doc, line[2]) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 16:52:44
|
Revision: 1167 Author: edloper Date: 2006-04-05 09:52:41 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1167&view=rev Log Message: ----------- - Added RoutineDoc.lineno - Added DocIndex.callers and DocIndex.callees, which record which functions have called which other functions. These variables are initialized from pstat profiling information, via the function DocIndex.read_profiling_info() - Fixed grouping comment typo Modified Paths: -------------- trunk/epydoc/src/epydoc/apidoc.py Modified: trunk/epydoc/src/epydoc/apidoc.py =================================================================== --- trunk/epydoc/src/epydoc/apidoc.py 2006-04-05 16:49:40 UTC (rev 1166) +++ trunk/epydoc/src/epydoc/apidoc.py 2006-04-05 16:52:41 UTC (rev 1167) @@ -36,12 +36,12 @@ ## Imports ###################################################################### -import types, re +import types, re, os.path from epydoc import log import epydoc import __builtin__ from epydoc.compat import * # Backwards compatibility -from epydoc.util import decode_with_backslashreplace +from epydoc.util import decode_with_backslashreplace, py_src_filename ###################################################################### # Dotted Names @@ -695,7 +695,7 @@ by the imported value's C{ValueDoc} by L{link_imports()<docbuilder.link_imports>}. @type: L{DottedName}""" - #{ end of "information about imported variables" group + #} end of "information about imported variables" group #: @ivar: #: This is currently used to extract values from __all__, etc, in @@ -1292,6 +1292,12 @@ """@ivar: The name of the routine's keyword argument, or C{None} if it has no keyword argument. @type: C{string} or C{None}""" + lineno = UNKNOWN # used to look up profiling info from pstats. + """@ivar: The line number of the first line of the function's + signature. For Python functions, this is equal to + C{func.func_code.co_firstlineno}. The first line of a file + is considered line 1. + @type: C{int}""" #} end of "signature" group #{ Information Extracted from Docstrings @@ -1448,6 +1454,20 @@ # submodules when appropriate.) self.root = sorted(root, key=lambda d:len(d.canonical_name)) + self.callers = None + """A dictionary mapping from C{RoutineDoc}s in this index + to lists of C{RoutineDoc}s for the routine's callers. + This dictionary is initialized by calling + L{read_profiling_info()}. + @type: C{list} of L{RoutineDoc}""" + + self.callees = None + """A dictionary mapping from C{RoutineDoc}s in this index + to lists of C{RoutineDoc}s for the routine's callees. + This dictionary is initialized by calling + L{read_profiling_info()}. + @type: C{list} of L{RoutineDoc}""" + #//////////////////////////////////////////////////////////// # Lookup methods #//////////////////////////////////////////////////////////// @@ -1611,6 +1631,60 @@ parent = api_doc.canonical_name.container() return self.get_valdoc(parent) + #//////////////////////////////////////////////////////////// + # Profiling information + #//////////////////////////////////////////////////////////// + + def read_profiling_info(self, profile_stats): + """ + Initialize the L{callers} and L{callees} variables, given a + C{Stat} object from the C{pstats} module. + + @warning: This method uses undocumented data structures inside + of C{profile_stats}. + """ + if self.callers is None: self.callers = {} + if self.callees is None: self.callees = {} + + # The Stat object encodes functions using `funcid`s, or + # tuples of (filename, lineno, funcname). Create a mapping + # from these `funcid`s to `RoutineDoc`s. + funcid_to_doc = self._get_funcid_to_doc_mapping(profile_stats) + + for callee, (cc, nc, tt, ct, callers) in profile_stats.stats.items(): + callee = funcid_to_doc.get(callee) + if callee is None: continue + for caller in callers: + caller = funcid_to_doc.get(caller) + if caller is None: continue + self.callers.setdefault(callee, []).append(caller) + self.callees.setdefault(caller, []).append(callee) + + def _get_funcid_to_doc_mapping(self, profile_stats): + """ + Return a dictionary mapping from C{pstat.Stat} funciton ids to + C{RoutineDoc}s. C{pstat.Stat} function ids are tuples of + C{(filename, lineno, funcname)}. + """ + # Maps (filename, lineno, funcname) -> RoutineDoc + funcid_to_doc = {} + for val_doc in self.reachable_valdocs(): + # We only care about routines. + if not isinstance(val_doc, RoutineDoc): continue + # Get the filename from the defining module. + module = val_doc.defining_module + if module is UNKNOWN or module.filename is UNKNOWN: continue + # Normalize the filename. + filename = os.path.abspath(module.filename) + try: filename = py_src_filename(filename) + except: pass + # Look up the stat_func_id + funcid = (filename, val_doc.lineno, val_doc.canonical_name[-1]) + if funcid in profile_stats.stats: + funcid_to_doc[funcid] = val_doc + + return funcid_to_doc + ###################################################################### ## Pretty Printing ###################################################################### This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 16:49:45
|
Revision: 1166 Author: edloper Date: 2006-04-05 09:49:40 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1166&view=rev Log Message: ----------- - Removed unused function Modified Paths: -------------- trunk/epydoc/src/epydoc/util.py Modified: trunk/epydoc/src/epydoc/util.py =================================================================== --- trunk/epydoc/src/epydoc/util.py 2006-04-05 02:35:07 UTC (rev 1165) +++ trunk/epydoc/src/epydoc/util.py 2006-04-05 16:49:40 UTC (rev 1166) @@ -80,30 +80,6 @@ return 'script-'+name ###################################################################### -## Backwards compatibility -###################################################################### - -def py_sorted(iterable, cmp=None, key=None, reverse=False): - """ - A python drop-in replacement for the builtin C{sorted}, which was - introduced in Python 2.4. This is used for backwards - compatibility. - """ - if key is None: - elts = list(iterable) - else: - elts = [(key(v), v) for v in iterable] - - if reverse: elts.reverse() - if cmp is None: elts.sort() - else: elts.sort(cmp) - - if key is None: - return elts - else: - return [v for (k,v) in elts] - -###################################################################### ## Text Processing ###################################################################### This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 02:35:14
|
Revision: 1165 Author: edloper Date: 2006-04-04 19:35:07 -0700 (Tue, 04 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1165&view=rev Log Message: ----------- - Add support for ansi color displays (e.g., OS X) to TerminalController Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2006-04-05 02:30:32 UTC (rev 1164) +++ trunk/epydoc/src/epydoc/cli.py 2006-04-05 02:35:07 UTC (rev 1165) @@ -584,7 +584,7 @@ A class that can be used to portably generate formatted output to a terminal. See U{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475116} - for documentation. + for documentation. (This is a somewhat stripped-down version.) """ BOL = '' #: Move the cursor to the beginning of the line UP = '' #: Move the cursor up one line @@ -602,6 +602,7 @@ BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 CLEAR_EOL=el BOLD=bold UNDERLINE=smul NORMAL=sgr0""".split() _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split() + _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split() def __init__(self, term_stream=sys.stdout): # If the stream isn't a tty, then assume it has no capabilities. @@ -635,6 +636,10 @@ if set_fg: for i,color in zip(range(len(self._COLORS)), self._COLORS): setattr(self, color, curses.tparm(set_fg, i) or '') + set_fg_ansi = self._tigetstr('setaf') + if set_fg_ansi: + for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): + setattr(self, color, curses.tparm(set_fg_ansi, i) or '') def _tigetstr(self, cap_name): # String capabilities can include "delays" of the form "$<2>". This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 02:30:38
|
Revision: 1164 Author: edloper Date: 2006-04-04 19:30:32 -0700 (Tue, 04 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1164&view=rev Log Message: ----------- - Added some docs - Added --separate-classes option - Added --no-sourcecode - Added target to just generate the latex output, without actually running latex on it. Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2006-04-05 02:28:48 UTC (rev 1163) +++ trunk/epydoc/src/epydoc/cli.py 2006-04-05 02:30:32 UTC (rev 1164) @@ -7,36 +7,48 @@ # $Id$ """ -Command-line interface for epydoc. +Command-line interface for epydoc. Abbreviated Usage:: -[xx] this usage message is probably a little out-of-date. - -Usage:: - - epydoc [OPTIONS] MODULES... + epydoc [options] NAMES... - MODULES... The Python modules to document. + NAMES... The Python modules to document. --html Generate HTML output (default). --latex Generate LaTeX output. --pdf Generate pdf output, via LaTeX. - --check Run documentation completeness checks. -o DIR, --output DIR The output directory. - -n NAME, --name NAME The documented project's name. - -u URL, --url URL The documented project's url. - -t PAGE, --top PAGE The top page for the HTML documentation. - -c SHEET, --css SHEET CSS stylesheet for HTML files. - --private-css SHEET CSS stylesheet for private objects. --inheritance STYLE The format for showing inherited objects. -V, --version Print the version of epydoc. - -h, -?, --help, --usage Display a usage message. - -h TOPIC, --help TOPIC Display information about TOPIC (docformat, - css, inheritance, usage, or version). + -h, --help Display a usage message. - Run \"epydoc --help\" for a complete option list. - See the epydoc(1) man page for more information. +Run \"epydoc --help\" for a complete option list. See the epydoc(1) +man page for more information. -Verbosity levels:: +Config Files +============ +Configuration files can be specified with the C{--config} option. +These files are read using U{ConfigParser +<http://docs.python.org/lib/module-ConfigParser.html>}. Configuration +files may set options or add names of modules to document. Option +names are (usually) identical to the long names of command line +options. To specify names to document, use any of the following +option names:: + module modules value values object objects + +A simple example of a config file is:: + + [epydoc] + modules: sys, os, os.path, re + name: Example + graph: classtree + introspect: no + +Verbosity Levels +================ +The C{-v} and C{-q} options increase and decrease verbosity, +respectively. The default verbosity level is zero. The verbosity +levels are currently defined as follows:: + Progress Markup warnings Warnings Errors -3 none no no no -2 none no no yes @@ -80,30 +92,20 @@ help="Write plaintext output. (not implemented yet)") action_group.add_option( # --latex "--latex", action="store_const", dest="action", const="latex", - help="Write LaTeX output. (not implemented yet)") + help="Write LaTeX output.") action_group.add_option( # --dvi "--dvi", action="store_const", dest="action", const="dvi", - help="Write DVI output. (not implemented yet)") + help="Write DVI output.") action_group.add_option( # --ps "--ps", action="store_const", dest="action", const="ps", - help="Write Postscript output. (not implemented yet)") + help="Write Postscript output.") action_group.add_option( # --pdf "--pdf", action="store_const", dest="action", const="pdf", - help="Write PDF output. (not implemented yet)") + help="Write PDF output.") action_group.add_option( # --check "--check", action="store_const", dest="action", const="check", help="Check completeness of docs. (not implemented yet)") - # Options I haven't ported over yet are... - # separate-classes (??) -- for latex only - # command-line-order (??) - # ignore-param-mismatch -- not implemented yet, but will be related - # to DocInheriter - # tests=... - # --no-markup-warnings ? - # --no-source, --incl-source? - - # Add options -- Options options_group.add_option( # --output "--output", "-o", dest="target", metavar="PATH", @@ -142,13 +144,13 @@ "of an HTML file -- navigation bars will be added to it.") options_group.add_option( # --frames "--show-frames", action="store_true", dest="show_frames", - help="Include frames in the output.") + help="Include frames in the HTML output. (default)") options_group.add_option( # --no-frames "--no-frames", action="store_false", dest="show_frames", - help="Do not include frames in the output.") + help="Do not include frames in the HTML output.") options_group.add_option( # --private "--show-private", action="store_true", dest="show_private", - help="Include private variables in the output.") + help="Include private variables in the output. (default)") options_group.add_option( # --no-private "--no-private", action="store_false", dest="show_private", help="Do not include private variables in the output.") @@ -157,7 +159,7 @@ help="List each module's imports.") options_group.add_option( # --show-imports "--no-imports", action="store_false", dest="show_imports", - help="Do not list each module's imports.") + help="Do not list each module's imports. (default)") options_group.add_option( # --quiet "--quiet", "-q", action="count", dest="quiet", help="Decrease the verbosity.") @@ -191,6 +193,20 @@ "to specify its location. This option may be repeated to " "include multiple graph types in the output. GRAPHTYPE" "should be one of: all, %s." % ', '.join(GRAPH_TYPES))) + options_group.add_option( + '--separate-classes', action='store_true', + dest='list_classes_separately', + help=("When generating LaTeX or PDF output, list each class in " + "its own section, instead of listing them under their " + "containing module.")) + options_group.add_option( + '--show-sourcecode', action='store_true', dest='include_source_code', + help=("Include source code with syntax highlighting in the " + "HTML output.")) + options_group.add_option( + '--no-sourcecode', action='store_false', dest='include_source_code', + help=("Do not include source code with syntax highlighting in the " + "HTML output.")) # Add the option groups. optparser.add_option_group(action_group) @@ -204,7 +220,8 @@ verbose=0, quiet=0, parse=True, introspect=True, debug=epydoc.DEBUG, profile=False, - graphs=[]) + graphs=[], list_classes_separately=False, + include_source_code=True) # Parse the arguments. options, names = optparser.parse_args() @@ -262,9 +279,10 @@ fp.close() for optname in configparser.options('epydoc'): val = configparser.get('epydoc', optname).strip() + optname = optname.lower().strip() if optname in ('modules', 'objects', 'values', 'module', 'object', 'value'): - names.extend(val.split()) + names.extend(val.replace(',', ' ').split()) elif optname in ('output', 'target'): options.target = val elif optname == 'inheritance': @@ -312,6 +330,10 @@ raise ValueError('"graph" expected one of: %s.' % ', '.join(GRAPH_TYPES)) options.graphs.extend(graphtypes) + elif optname in ('separate-classes', 'separate_classes'): + options.list_classes_separately = _str_to_bool(val, optname) + elif optname == 'sourcecode': + options.include_source_code = _str_to_bool(val, optname) else: raise ValueError('Unknown option %s' % optname) @@ -339,9 +361,6 @@ logger = ConsoleLogger(options.verbosity) log.register_logger(logger) else: - # Roughly how long does each action take? - action_time = {'html': [100], 'text': 30, 'latex': 60, 'ps': 70, - 'pdf': 80} # Each number is a rough approximation of how long we spend on # that task, used to divide up the unified progress bar. stages = [40, # Building documentation @@ -353,7 +372,8 @@ 2] # Sorting & Grouping if options.action == 'html': stages += [100] elif options.action == 'text': stages += [30] - elif options.action == 'latex': stages += [60,30] + elif options.action == 'latex': stages += [60] + elif options.action == 'dvi': stages += [60,30] elif options.action == 'ps': stages += [60,40] elif options.action == 'pdf': stages += [60,50] else: raise ValueError, '%r not supported' % options.action @@ -390,7 +410,7 @@ # Perform the specified action. if options.action == 'html': write_html(docindex, options) - elif options.action in ('latex', 'ps', 'pdf'): + elif options.action in ('latex', 'dvi', 'ps', 'pdf'): write_latex(docindex, options, options.action) elif options.action == 'text': write_text(docindex, options) @@ -430,8 +450,11 @@ log.start_progress('Writing LaTeX docs') latex_writer.write(options.target) log.end_progress() + # If we're just generating the latex, and not any output format, + # then we're done. + if format == 'latex': return - if format == 'latex': steps = 4 + if format == 'dvi': steps = 4 elif format == 'ps': steps = 5 elif format == 'pdf': steps = 6 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 02:28:52
|
Revision: 1163 Author: edloper Date: 2006-04-04 19:28:48 -0700 (Tue, 04 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1163&view=rev Log Message: ----------- - Fixed bug introduced by previous checkin Modified Paths: -------------- trunk/epydoc/src/epydoc/docstringparser.py Modified: trunk/epydoc/src/epydoc/docstringparser.py =================================================================== --- trunk/epydoc/src/epydoc/docstringparser.py 2006-04-05 01:58:40 UTC (rev 1162) +++ trunk/epydoc/src/epydoc/docstringparser.py 2006-04-05 02:28:48 UTC (rev 1163) @@ -251,7 +251,7 @@ # filename of its containing module. name = api_doc.canonical_name module = api_doc.defining_module - if module is not None and module.filename not in (None, UNKNOWN): + if module != UNKNOWN and module.filename not in (None, UNKNOWN): try: filename = py_src_filename(module.filename) except: filename = module.filename else: @@ -629,7 +629,7 @@ # Find the module that defines api_doc. module = api_doc.defining_module # Look up its docformat. - if module is not None and module.docformat not in (None, UNKNOWN): + if module != UNKNOWN and module.docformat not in (None, UNKNOWN): docformat = module.docformat else: docformat = DEFAULT_DOCFORMAT This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 01:58:45
|
Revision: 1162 Author: edloper Date: 2006-04-04 18:58:40 -0700 (Tue, 04 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1162&view=rev Log Message: ----------- - Added VariableDoc.defining_module property (based on its container) - Removed DocIndex.module_that_defines; use the defining_module property instead. Modified Paths: -------------- trunk/epydoc/src/epydoc/apidoc.py trunk/epydoc/src/epydoc/docstringparser.py trunk/epydoc/src/epydoc/docwriter/html.py Modified: trunk/epydoc/src/epydoc/apidoc.py =================================================================== --- trunk/epydoc/src/epydoc/apidoc.py 2006-04-05 01:51:56 UTC (rev 1161) +++ trunk/epydoc/src/epydoc/apidoc.py 2006-04-05 01:58:40 UTC (rev 1162) @@ -612,6 +612,15 @@ container's cannonical name, and adding the variable's name to it.""") + def _get_defining_module(self): + if self.container is UNKNOWN: + return UNKNOWN + return self.container.defining_module + defining_module = property(_get_defining_module, doc=""" + A read-only property that can be used to get the variable's + defining module. This is defined as the defining module + of the variable's container.""") + def apidoc_links(self, **filters): if self.value in (None, UNKNOWN): return [] @@ -1602,23 +1611,6 @@ parent = api_doc.canonical_name.container() return self.get_valdoc(parent) - # [xx] this could be moved out of docindex and/or removed. - def module_that_defines(self, api_doc): - """ - Return the C{ModuleDoc} of the module that defines C{api_doc}, - or C{None} if that module is not in the index. If C{api_doc} - is itself a module, then C{api_doc} will be returned. - """ - if isinstance(api_doc, VariableDoc): - api_doc = api_doc.container - if api_doc.defining_module == UNKNOWN: return None - if not isinstance(api_doc.defining_module, ModuleDoc): - log.error("Internal error -- %r claims that its defining " - "module is %r, but the latter is not a module." % - (api_doc, api_doc.defining_module)) - return None - return api_doc.defining_module - ###################################################################### ## Pretty Printing ###################################################################### Modified: trunk/epydoc/src/epydoc/docstringparser.py =================================================================== --- trunk/epydoc/src/epydoc/docstringparser.py 2006-04-05 01:51:56 UTC (rev 1161) +++ trunk/epydoc/src/epydoc/docstringparser.py 2006-04-05 01:58:40 UTC (rev 1162) @@ -250,7 +250,7 @@ # Get the name of the item containing the error, and the # filename of its containing module. name = api_doc.canonical_name - module = docindex.module_that_defines(api_doc) + module = api_doc.defining_module if module is not None and module.filename not in (None, UNKNOWN): try: filename = py_src_filename(module.filename) except: filename = module.filename @@ -627,7 +627,7 @@ parse the API documentation for the given object. """ # Find the module that defines api_doc. - module = docindex.module_that_defines(api_doc) + module = api_doc.defining_module # Look up its docformat. if module is not None and module.docformat not in (None, UNKNOWN): docformat = module.docformat Modified: trunk/epydoc/src/epydoc/docwriter/html.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html.py 2006-04-05 01:51:56 UTC (rev 1161) +++ trunk/epydoc/src/epydoc/docwriter/html.py 2006-04-05 01:58:40 UTC (rev 1162) @@ -2758,8 +2758,8 @@ else: return None else: - module = self.docindex.module_that_defines(api_doc) - if module is None: return None + module = self.docindex.defining_module + if module == UNKNOWN: return None module_pysrc_url = self.pysrc_url(module) if module_pysrc_url is None: return None module_name = module.canonical_name This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 01:52:00
|
Revision: 1161 Author: edloper Date: 2006-04-04 18:51:56 -0700 (Tue, 04 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1161&view=rev Log Message: ----------- - Removed sorted_by_name arg from reachable_valdocs; use sorted() on the result instead Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/html.py trunk/epydoc/src/epydoc/docwriter/latex.py Modified: trunk/epydoc/src/epydoc/docwriter/html.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html.py 2006-04-05 01:51:34 UTC (rev 1160) +++ trunk/epydoc/src/epydoc/docwriter/html.py 2006-04-05 01:51:56 UTC (rev 1161) @@ -369,8 +369,8 @@ # Precompute lists & sets of APIDoc objects that we're # interested in. self.valdocs = valdocs = sorted(docindex.reachable_valdocs( - sorted_by_name=True, imports=False, packages=False, bases=False, - submodules=False, subclasses=False, private=self._show_private)) + imports=False, packages=False, bases=False, submodules=False, + subclasses=False, private=self._show_private)) self.module_list = [d for d in valdocs if isinstance(d, ModuleDoc)] """The list of L{ModuleDoc}s for the documented modules.""" self.module_set = set(self.module_list) Modified: trunk/epydoc/src/epydoc/docwriter/latex.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/latex.py 2006-04-05 01:51:34 UTC (rev 1160) +++ trunk/epydoc/src/epydoc/docwriter/latex.py 2006-04-05 01:51:56 UTC (rev 1161) @@ -80,9 +80,9 @@ self._index_functions = 1 self._hyperref = 1 self._encoding = kwargs.get('encoding', 'latin1') - self.valdocs = docindex.reachable_valdocs( - sorted_by_name=True, imports=False, packages=False, bases=False, - submodules=False, subclasses=False, private=self._show_private) + self.valdocs = sorted(docindex.reachable_valdocs( + imports=False, packages=False, bases=False, submodules=False, + subclasses=False, private=self._show_private)) self._num_files = self.num_files() # For use with select_variables(): if self._show_private: self._public_filter = None This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 01:51:37
|
Revision: 1160 Author: edloper Date: 2006-04-04 18:51:34 -0700 (Tue, 04 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1160&view=rev Log Message: ----------- - Docstring updates - Removed some debug printfs - APIDoc objects are now ordered by canonical name (with __cmp__) - Removed sorted_by_name arg from reachable_valdocs; use sorted() on the result instead Modified Paths: -------------- trunk/epydoc/src/epydoc/apidoc.py Modified: trunk/epydoc/src/epydoc/apidoc.py =================================================================== --- trunk/epydoc/src/epydoc/apidoc.py 2006-04-04 16:28:13 UTC (rev 1159) +++ trunk/epydoc/src/epydoc/apidoc.py 2006-04-05 01:51:34 UTC (rev 1160) @@ -8,7 +8,6 @@ """ Classes for encoding API documentation about Python programs. - These classes are used as a common representation for combining information derived from introspection and from parsing. @@ -48,10 +47,6 @@ # Dotted Names ###################################################################### -# [xx] define a new exception specifically for bad dotted names? -# ValueError is a bit generic, and there are a couple places where -# I catch this a bit far from where it gets raised. - class DottedName: """ A sequence of identifiers, separated by periods, used to name a @@ -100,12 +95,10 @@ elif isinstance(piece, basestring): for subpiece in piece.split('.'): if not self._IDENTIFIER_RE.match(subpiece): - log.debug('Bad identifier %r' % (piece,)) raise DottedName.InvalidDottedName( 'Bad identifier %r' % (piece,)) self._identifiers.append(subpiece) else: - log.debug('Bad identifier %r' % (piece,)) raise DottedName.InvalidDottedName( 'Bad identifier %r' % (piece,)) self._identifiers = tuple(self._identifiers) @@ -135,6 +128,10 @@ return DottedName(self, *other) def __radd__(self, other): + """ + Return a new C{DottedName} whose identifier sequence is formed + by adding C{self}'s identifier sequence to C{other}'s. + """ if isinstance(other, (basestring, DottedName)): return DottedName(other, self) else: @@ -143,7 +140,9 @@ def __getitem__(self, i): """ Return the C{i}th identifier in this C{DottedName}. If C{i} is - a slice, then return a C{DottedName} + a non-empty slice, then return a C{DottedName} built from the + identifiers selected by the slice. If C{i} is an empty slice, + return an empty list (since empty C{DottedName}s are not valid). """ if isinstance(i, types.SliceType): pieces = self._identifiers[i.start:i.stop] @@ -159,6 +158,8 @@ """ Compare this dotted name to C{other}. Two dotted names are considered equal if their identifier subsequences are equal. + Ordering between dotted names is lexicographic, in order of + identifier from left to right. """ if not isinstance(other, DottedName): return -1 @@ -183,16 +184,31 @@ return DottedName(*self._identifiers[:-1]) def dominates(self, name, strict=False): + """ + Return true if this dotted name is equal to a prefix of + C{name}. If C{strict} is true, then also require that + C{self!=name}. + + >>> DottedName('a.b').dominates(DottedName('a.b.c.d')) + True + """ if strict and len(self._identifiers)==len(name._identifiers): return False return self._identifiers == name._identifiers[:len(self)] def contextualize(self, context): """ - Return a reduced version of a dotted name. This name may be ambigious - (it is if its name is the same of the context), but is should be - explicit enough for humans, and may be considerably shorter and more - readable than a fully qualified one. + If C{self} and C{context} share a common ancestor, then return + a name for C{self}, relative to that ancestor. If they do not + share a common ancestor (or if C{context} is C{UNKNOWN}), then + simply return C{self}. + + This is used to generate shorter versions of dotted names in + cases where users can infer the intended target from the + context. + + @type context: L{DottedName} + @rtype: L{DottedName} """ if context is UNKNOWN or not context or len(self) <= 1: return self @@ -317,18 +333,23 @@ not correspond to a valid attribute for this (sub)class of C{APIDoc}. """ - for key in kwargs: - if not hasattr(self, key): - raise TypeError('%s got unexpected arg %r' % - (self.__class__.__name__, key)) + if epydoc.DEBUG: + for key in kwargs: + if not hasattr(self.__class__, key): + raise TypeError('%s got unexpected arg %r' % + (self.__class__.__name__, key)) self.__dict__.update(kwargs) def _debug_setattr(self, attr, val): """ - Modify an C{APIDoc}'s attribute. + Modify an C{APIDoc}'s attribute. This is used when + L{epydoc.DEBUG} is true, to make sure we don't accidentally + set any inappropriate attributes on C{APIDoc} objects. @raise AttributeError: If C{attr} is not a valid attribute for - this (sub)class of C{APIDoc}. + this (sub)class of C{APIDoc}. (C{attr} is considered a + valid attribute iff C{self.__class__} defines an attribute + with that name.) """ # Don't intercept special assignments like __class__, or # assignments to private variables. @@ -351,20 +372,32 @@ information contained in this C{APIDoc}. """ return pp_apidoc(self, doublespace, depth, exclude, include) - - def __str__(self): - return self.pp() + __str__ = pp def specialize_to(self, cls): + """ + Change C{self}'s class to C{cls}. C{cls} must be a subclass + of C{self}'s current class. For example, if a generic + C{ValueDoc} was created for a value, and it is determined that + the value is a routine, you can update its class with: + + >>> valdoc.specialize_to(RoutineDoc) + """ if not issubclass(cls, self.__class__): - raise ValueError, 'xxx' + raise ValueError('Can not specialize to %r' % cls) + # Update the class. self.__class__ = cls - - # DO THIS [xx]? + # Update the class of any other apidoc's in the mergeset. + if self.__mergeset is not None: + for apidoc in self.__mergeset: + apidoc.__class__ = cls + # Re-initialize self, in case the subclass constructor does + # any special processing on its arguments. self.__init__(**self.__dict__) - # [xx] consider making them un-hashable?? __has_been_hashed = False + """True iff L{self.__hash__()} has ever been called.""" + def __hash__(self): self.__has_been_hashed = True return id(self.__dict__) @@ -372,9 +405,15 @@ def __cmp__(self, other): if not isinstance(other, APIDoc): return -1 if self.__dict__ is other.__dict__: return 0 - else: return -1 - + name_cmp = cmp(self.canonical_name, other.canonical_name) + if name_cmp == 0: return -1 + else: return name_cmp + __mergeset = None + """The set of all C{APIDoc} objects that have been merged with + this C{APIDoc} (using L{merge_and_overwrite()}). Each C{APIDoc} + in this set shares a common instance dictionary (C{__dict__}).""" + def merge_and_overwrite(self, other, ignore_hash_conflict=False): """ Combine C{self} and C{other} into a X{merged object}, such @@ -443,15 +482,11 @@ """ return [] -# [xx] use sorted() to replace sorted_by_name, and make -# APIDoc.__cmp__ use the name?? -def reachable_valdocs(root, sorted_by_name=False, **filters): +def reachable_valdocs(root, **filters): """ Return a list of all C{ValueDoc}s that can be reached, directly or indirectly from the given root list of C{ValueDoc}s. - @param sorted_by_name: If true, then sort the list of reachable - C{ValueDoc}s by name before returning it. @param filters: A set of filters that can be used to prevent C{reachable_valdocs} from following specific link types when looking for C{ValueDoc}s that can be reached from the root @@ -461,9 +496,7 @@ apidoc_queue = list(root) val_set = set() var_set = set() - #if 'private' in filters: log.debug('RR', root) while apidoc_queue: - #if 'private' in filters: log.debug(' ', apidoc_queue) api_doc = apidoc_queue.pop() if isinstance(api_doc, ValueDoc): val_set.add(api_doc) @@ -471,10 +504,7 @@ var_set.add(api_doc) apidoc_queue.extend([v for v in api_doc.apidoc_links(**filters) if v not in val_set and v not in var_set]) - if sorted_by_name: - return sorted(val_set, key=lambda d:d.canonical_name) - else: - return val_set + return val_set ###################################################################### # Variable Documentation Objects @@ -1542,20 +1572,18 @@ # etc #//////////////////////////////////////////////////////////// - def reachable_valdocs(self, sorted_by_name=False, **filters): + def reachable_valdocs(self, **filters): """ Return a list of all C{ValueDoc}s that can be reached, directly or indirectly from this C{DocIndex}'s root set. - @param sorted_by_name: If true, then sort the list of - reachable C{ValueDoc}s by name before returning it. @param filters: A set of filters that can be used to prevent C{reachable_valdocs} from following specific link types when looking for C{ValueDoc}s that can be reached from the root set. See C{APIDoc.apidoc_links} for a more complete description. """ - return reachable_valdocs(self.root, sorted_by_name, **filters) + return reachable_valdocs(self.root, **filters) def container(self, api_doc): """ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-04 16:28:20
|
Revision: 1159 Author: edloper Date: 2006-04-04 09:28:13 -0700 (Tue, 04 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1159&view=rev Log Message: ----------- - Fixed sf bug #1464341: Option files not closed after readfp() - Removed 2 debug printfs Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2006-04-04 15:19:28 UTC (rev 1158) +++ trunk/epydoc/src/epydoc/cli.py 2006-04-04 16:28:13 UTC (rev 1159) @@ -257,10 +257,9 @@ # ConfigParser.read() silently ignores errors, so open the files # manually (since we want to notify the user of any errors). for configfile in configfiles: - log.debug('here') fp = open(configfile, 'r') # may raise IOError. - log.debug('here2') configparser.readfp(fp, configfile) + fp.close() for optname in configparser.options('epydoc'): val = configparser.get('epydoc', optname).strip() if optname in ('modules', 'objects', 'values', This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |