Skip to content
Browse files

removing dependency on build-tools

  • Loading branch information...
1 parent 3960fd9 commit 781e1ae662e23f08b1b37cae7cea5bb1d45a6834 Alex Boyd committed Aug 20, 2012
Showing with 22,947 additions and 11 deletions.
  1. +0 −3 build.gradle
  2. +0 −1 gradle.properties
  3. +1 −0 vertx-core/build.gradle
  4. +4 −1 vertx-lang/vertx-lang-groovy/build.gradle
  5. +1 −0 vertx-lang/vertx-lang-java/build.gradle
  6. +28 −4 vertx-lang/vertx-lang-jruby/build.gradle
  7. +30 −2 vertx-lang/vertx-lang-jython/build.gradle
  8. +21 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/LICENSE.txt
  9. +57 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/README.txt
  10. +228 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/__init__.py
  11. +2,220 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/apidoc.py
  12. +349 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/checker.py
  13. +1,666 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/cli.py
  14. +250 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/compat.py
  15. +1,422 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docbuilder.py
  16. +1,061 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docintrospecter.py
  17. +2,192 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docparser.py
  18. +1,111 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docstringparser.py
  19. +12 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docwriter/__init__.py
  20. +1,618 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docwriter/dotgraph.py
  21. +3,517 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docwriter/html.py
  22. +915 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docwriter/html_colorize.py
  23. +550 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docwriter/html_css.py
  24. +190 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docwriter/html_help.py
  25. +1,313 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docwriter/latex.py
  26. +1,098 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docwriter/latex_sty.py
  27. +277 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docwriter/plaintext.py
  28. +512 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/docwriter/xlink.py
  29. +1,148 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/gui.py
  30. +206 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/log.py
  31. +634 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/markup/__init__.py
  32. +316 −0 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/markup/doctest.py
Sorry, we could not display the entire diff because it was too big.
View
3 build.gradle
@@ -22,9 +22,6 @@ buildscript {
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
mavenCentral()
}
- dependencies {
- classpath "org.vert-x:build-tools:$vertxBuildToolsVersion"
- }
}
allprojects {
View
1 gradle.properties
@@ -15,7 +15,6 @@
#
# gradle version
gradleVersion=1.1
-vertxBuildToolsVersion=0.1-SNAPSHOT
# vert.x version
version=1.2.3.final
View
1 vertx-core/build.gradle
@@ -24,6 +24,7 @@ dependencies {
compile "com.hazelcast:hazelcast:$hazelcastVersion"
compile "io.netty:netty:$nettyVersion"
}
+
artifacts {
platform jar
}
View
5 vertx-lang/vertx-lang-groovy/build.gradle
@@ -16,18 +16,21 @@
apply from: "$rootDir/gradle/maven.gradle"
apply plugin: 'groovy'
-apply plugin: 'vertx-groovy'
dependencies {
compile project(':vertx-core')
compile project(':vertx-platform')
+
groovy "org.codehaus.groovy:groovy-all:$groovyVersion"
}
+
artifacts {
platform jar
}
+
groovydoc {
destinationDir = file("build/docs/groovy/api")
outputs.dir destinationDir
}
+
javadoc.dependsOn groovydoc
View
1 vertx-lang/vertx-lang-java/build.gradle
@@ -20,6 +20,7 @@ dependencies {
compile project(':vertx-core')
compile project(':vertx-platform')
}
+
artifacts {
platform jar
}
View
32 vertx-lang/vertx-lang-jruby/build.gradle
@@ -15,16 +15,40 @@
*/
apply from: "$rootDir/gradle/maven.gradle"
-apply plugin: 'vertx-jruby'
+sourceSets {
+ main {
+ resources {
+ srcDirs project.file("src/main/ruby_scripts")
+ }
+ }
+ test {
+ resources {
+ srcDirs project.file("src/test/ruby_scripts")
+ }
+ }
+}
+
dependencies {
compile project(':vertx-core')
compile project(':vertx-platform')
- vertxJRuby "org.jruby:jruby:$jrubyVersion"
+ compile "org.jruby:jruby:$jrubyVersion"
}
+
artifacts {
platform jar
}
-yardoc.readme = "$rootDir/README.md"
-yardoc.license = "$rootDir/LICENSE.txt"
+task yardoc(type:Exec) {
+ commandLine = [ "yardoc",
+ "--title", "Ruby API Doc",
+ "--no-private",
+ "--quiet",
+ "--markup-provider", "kramdown",
+ "--readme", "$rootDir/README.md",
+ "--output-dir","${project.buildDir}/docs/ruby/api",
+ "src/main/ruby/**/*.rb", "src/main/ruby_scripts/**/*.rb",
+ "-", "$rootDir/LICENSE.txt" ]
+}
+
+javadoc.dependsOn yardoc
View
32 vertx-lang/vertx-lang-jython/build.gradle
@@ -15,13 +15,41 @@
*/
apply from: "$rootDir/gradle/maven.gradle"
-apply plugin: 'vertx-jython'
+
+sourceSets {
+ main {
+ resources {
+ srcDirs project.file("src/main/python_scripts")
+ exclude '**/*$py.class'
+ }
+ }
+ test {
+ resources {
+ srcDirs project.file("src/test/python_scripts")
+ }
+ }
+}
dependencies {
compile project(':vertx-core')
compile project(':vertx-platform')
- vertxJython "org.python:jython-standalone:$jythonVersion"
+ compile "org.python:jython-standalone:$jythonVersion"
}
+
artifacts {
platform jar
}
+
+task pydoc(type: Exec) {
+ ignoreExitValue true
+ workingDir = project.projectDir
+
+ commandLine = [ System.getenv()['JYTHON_HOME'] + "jython",
+ "-J-classpath", project.files(project.configurations.compile).files.join(':'),
+ "-Dproj.base=${project.projectDir}",
+ "-Dpython.path=${project.projectDir}/src/build_tools/doclib:src/main/python_scripts",
+ "-Dproj.base=$project.projectDir.path",
+ "${project.projectDir}/src/build_tools/pydocx.py" ]
+}
+
+javadoc.dependsOn pydoc
View
21 vertx-lang/vertx-lang-jython/src/build_tools/doclib/LICENSE.txt
@@ -0,0 +1,21 @@
+epydoc is released under the following license:
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and any associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ The software is provided "as is", without warranty of any
+ kind, express or implied, including but not limited to the
+ warranties of merchantability, fitness for a particular purpose
+ and noninfringement. In no event shall the authors or copyright
+ holders be liable for any claim, damages or other liability,
+ whether in an action of contract, tort or otherwise, arising from,
+ out of or in connection with the software or the use or other
+ dealings in the software.
View
57 vertx-lang/vertx-lang-jython/src/build_tools/doclib/README.txt
@@ -0,0 +1,57 @@
+###############################################################
+### Epydoc ###
+###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~###
+### Copyright (C) Edward Loper ###
+### Author: Edward Loper <edloper@gradient.cis.upenn.edu> ###
+### URL: <http://epydoc.sourceforge.net> ###
+### For license information, see LICENSE.TXT ###
+###############################################################
+
+Introduction
+~~~~~~~~~~~~
+ Epydoc is a tool for generating API documentation for Python
+ modules, based on their docstrings. A lightweight markup language
+ called epytext can be used to format docstrings, and to add
+ information about specific fields, such as parameters and instance
+ variables.
+
+Documentation
+~~~~~~~~~~~~~
+ Documentation for epydoc, including installation and usage
+ instructions, and a complete description of the epytext markup
+ language, is available on the epydoc homepage:
+
+ <http://epydoc.sourceforge.net/>
+
+ This documentation is also available in the doc/ subdirectory of
+ the source distribution.
+
+Installing
+~~~~~~~~~~
+ To install epydoc, use make:
+
+ [user epydoc-3.0]$ su
+ Password:
+ [root epydoc-3.0]# make install
+ [root epydoc-3.0]# make installdocs
+
+ Or use the distutils setup.py script:
+
+ [user epydoc-3.0]$ su
+ Password:
+ [root epydoc-3.0]# python setup.py install
+
+ For complete installation instructions, including instructions on
+ how to install from RPM package, Debian package, or the windows
+ installer, see the epydoc homepage:
+
+ <http://epydoc.sourceforge.net/installing.html>
+
+Usage
+~~~~~
+ Run "epydoc --help" for a description of epydoc's usage.
+
+Contributing
+~~~~~~~~~~~~
+ If you are interested in contributing to epydoc, please email
+ <edloper@gradient.cis.upenn.edu>.
View
228 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/__init__.py
@@ -0,0 +1,228 @@
+# epydoc
+#
+# Copyright (C) 2005 Edward Loper
+# Author: Edward Loper <edloper@loper.org>
+# URL: <http://epydoc.sf.net>
+#
+# $Id: __init__.py 1750 2008-02-23 16:09:47Z edloper $
+
+"""
+Automatic Python reference documentation generator. Epydoc processes
+Python modules and docstrings to generate formatted API documentation,
+in the form of HTML pages. Epydoc can be used via a command-line
+interface (`epydoc.cli`) and a graphical interface (`epydoc.gui`).
+Both interfaces let the user specify a set of modules or other objects
+to document, and produce API documentation using the following steps:
+
+1. Extract basic information about the specified objects, and objects
+ that are related to them (such as the values defined by a module).
+ This can be done via introspection, parsing, or both:
+
+ * *Introspection* imports the objects, and examines them directly
+ using Python's introspection mechanisms.
+
+ * *Parsing* reads the Python source files that define the objects,
+ and extracts information from those files.
+
+2. Combine and process that information.
+
+ * **Merging**: Merge the information obtained from introspection &
+ parsing each object into a single structure.
+
+ * **Linking**: Replace any \"pointers\" that were created for
+ imported variables with the documentation that they point to.
+
+ * **Naming**: Assign unique *canonical names* to each of the
+ specified objects, and any related objects.
+
+ * **Docstrings**: Parse the docstrings of each of the specified
+ objects.
+
+ * **Inheritance**: Add variables to classes for any values that
+ they inherit from their base classes.
+
+3. Generate output. Output can be generated in a variety of formats:
+
+ * An HTML webpage.
+
+ * A LaTeX document (which can be rendered as a PDF file)
+
+ * A plaintext description.
+
+.. digraph:: Overview of epydoc's architecture
+ :caption: The boxes represent steps in epydoc's processing chain.
+ Arrows are annotated with the data classes used to
+ communicate between steps. The lines along the right
+ side mark what portions of the processing chain are
+ initiated by build_doc_index() and cli(). Click on
+ any item to see its documentation.
+
+ /*
+ Python module or value * *
+ / \ | |
+ V V | |
+ introspect_docs() parse_docs() | |
+ \ / | |
+ V V | |
+ merge_docs() | |
+ | build_doc_index() cli()
+ V | |
+ link_imports() | |
+ | | |
+ V | |
+ assign_canonical_names() | |
+ | | |
+ V | |
+ parse_docstrings() | |
+ | | |
+ V | |
+ inherit_docs() * |
+ / | \ |
+ V V V |
+ HTMLWriter LaTeXWriter PlaintextWriter *
+ */
+
+ ranksep = 0.1;
+ node [shape="box", height="0", width="0"]
+
+ { /* Task nodes */
+ node [fontcolor=\"#000060\"]
+ introspect [label="Introspect value:\\nintrospect_docs()",
+ href="<docintrospecter.introspect_docs>"]
+ parse [label="Parse source code:\\nparse_docs()",
+ href="<docparser.parse_docs>"]
+ merge [label="Merge introspected & parsed docs:\\nmerge_docs()",
+ href="<docbuilder.merge_docs>", width="2.5"]
+ link [label="Link imports:\\nlink_imports()",
+ href="<docbuilder.link_imports>", width="2.5"]
+ name [label="Assign names:\\nassign_canonical_names()",
+ href="<docbuilder.assign_canonical_names>", width="2.5"]
+ docstrings [label="Parse docstrings:\\nparse_docstring()",
+ href="<docstringparser.parse_docstring>", width="2.5"]
+ inheritance [label="Inherit docs from bases:\\ninherit_docs()",
+ href="<docbuilder.inherit_docs>", width="2.5"]
+ write_html [label="Write HTML output:\\nHTMLWriter",
+ href="<docwriter.html>"]
+ write_latex [label="Write LaTeX output:\\nLaTeXWriter",
+ href="<docwriter.latex>"]
+ write_text [label="Write text output:\\nPlaintextWriter",
+ href="<docwriter.plaintext>"]
+ }
+
+ { /* Input & Output nodes */
+ node [fontcolor=\"#602000\", shape="plaintext"]
+ input [label="Python module or value"]
+ output [label="DocIndex", href="<apidoc.DocIndex>"]
+ }
+
+ { /* Graph edges */
+ edge [fontcolor=\"#602000\"]
+ input -> introspect
+ introspect -> merge [label="APIDoc", href="<apidoc.APIDoc>"]
+ input -> parse
+ parse -> merge [label="APIDoc", href="<apidoc.APIDoc>"]
+ merge -> link [label=" DocIndex", href="<apidoc.DocIndex>"]
+ link -> name [label=" DocIndex", href="<apidoc.DocIndex>"]
+ name -> docstrings [label=" DocIndex", href="<apidoc.DocIndex>"]
+ docstrings -> inheritance [label=" DocIndex", href="<apidoc.DocIndex>"]
+ inheritance -> output
+ output -> write_html
+ output -> write_latex
+ output -> write_text
+ }
+
+ { /* Task collections */
+ node [shape="circle",label="",width=.1,height=.1]
+ edge [fontcolor="black", dir="none", fontcolor=\"#000060\"]
+ l3 -> l4 [label=" epydoc.\\l docbuilder.\\l build_doc_index()",
+ href="<docbuilder.build_doc_index>"]
+ l1 -> l2 [label=" epydoc.\\l cli()", href="<cli>"]
+ }
+ { rank=same; l1 l3 input }
+ { rank=same; l2 write_html }
+ { rank=same; l4 output }
+
+Package Organization
+====================
+The epydoc package contains the following subpackages and modules:
+
+.. packagetree::
+ :style: UML
+
+The user interfaces are provided by the `gui` and `cli` modules.
+The `apidoc` module defines the basic data types used to record
+information about Python objects. The programmatic interface to
+epydoc is provided by `docbuilder`. Docstring markup parsing is
+handled by the `markup` package, and output generation is handled by
+the `docwriter` package. See the submodule list for more
+information about the submodules and subpackages.
+
+:group User Interface: gui, cli
+:group Basic Data Types: apidoc
+:group Documentation Generation: docbuilder, docintrospecter, docparser
+:group Docstring Processing: docstringparser, markup
+:group Output Generation: docwriter
+:group Completeness Checking: checker
+:group Miscellaneous: log, util, test, compat
+
+:author: `Edward Loper <edloper@gradient.cis.upenn.edu>`__
+:requires: Python 2.3+
+:version: 3.0.1
+:see: `The epydoc webpage <http://epydoc.sourceforge.net>`__
+:see: `The epytext markup language
+ manual <http://epydoc.sourceforge.net/epytext.html>`__
+
+:todo: Create a better default top_page than trees.html.
+:todo: Fix trees.html to work when documenting non-top-level
+ modules/packages
+:todo: Implement @include
+:todo: Optimize epytext
+:todo: More doctests
+:todo: When introspecting, limit how much introspection you do (eg,
+ don't construct docs for imported modules' vars if it's
+ not necessary)
+
+:bug: UserDict.* is interpreted as imported .. why??
+
+:license: IBM Open Source License
+:copyright: |copy| 2006 Edward Loper
+
+:newfield contributor: Contributor, Contributors (Alphabetical Order)
+:contributor: `Glyph Lefkowitz <mailto:glyph@twistedmatrix.com>`__
+:contributor: `Edward Loper <mailto:edloper@gradient.cis.upenn.edu>`__
+:contributor: `Bruce Mitchener <mailto:bruce@cubik.org>`__
+:contributor: `Jeff O'Halloran <mailto:jeff@ohalloran.ca>`__
+:contributor: `Simon Pamies <mailto:spamies@bipbap.de>`__
+:contributor: `Christian Reis <mailto:kiko@async.com.br>`__
+:contributor: `Daniele Varrazzo <mailto:daniele.varrazzo@gmail.com>`__
+:contributor: `Jonathan Guyer <mailto:guyer@nist.gov>`__
+
+.. |copy| unicode:: 0xA9 .. copyright sign
+"""
+__docformat__ = 'restructuredtext en'
+
+__version__ = '3.0.1'
+"""The version of epydoc"""
+
+__author__ = 'Edward Loper <edloper@gradient.cis.upenn.edu>'
+"""The primary author of eypdoc"""
+
+__url__ = 'http://epydoc.sourceforge.net'
+"""The URL for epydoc's homepage"""
+
+__license__ = 'IBM Open Source License'
+"""The license governing the use and distribution of epydoc"""
+
+# [xx] this should probably be a private variable:
+DEBUG = False
+"""True if debugging is turned on."""
+
+# Changes needed for docs:
+# - document the method for deciding what's public/private
+# - epytext: fields are defined slightly differently (@group)
+# - new fields
+# - document __extra_epydoc_fields__ and @newfield
+# - Add a faq?
+# - @type a,b,c: ...
+# - new command line option: --command-line-order
+
View
2,220 vertx-lang/vertx-lang-jython/src/build_tools/doclib/epydoc/apidoc.py
@@ -0,0 +1,2220 @@
+# epydoc -- API Documentation Classes
+#
+# Copyright (C) 2005 Edward Loper
+# Author: Edward Loper <edloper@loper.org>
+# URL: <http://epydoc.sf.net>
+#
+# $Id: apidoc.py 1811 2009-02-03 21:29:51Z edloper $
+
+"""
+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.
+
+The API documentation for a Python program is encoded using a graph of
+L{APIDoc} objects, each of which encodes information about a single
+Python variable or value. C{APIDoc} has two direct subclasses:
+L{VariableDoc}, for documenting variables; and L{ValueDoc}, for
+documenting values. The C{ValueDoc} class is subclassed further, to
+define the different pieces of information that should be recorded
+about each value type:
+
+G{classtree: APIDoc}
+
+The distinction between variables and values is intentionally made
+explicit. This allows us to distinguish information about a variable
+itself (such as whether it should be considered 'public' in its
+containing namespace) from information about the value it contains
+(such as what type the value has). This distinction is also important
+because several variables can contain the same value: each variable
+should be described by a separate C{VariableDoc}; but we only need one
+C{ValueDoc}, since they share a single value.
+
+@todo: Add a cache to canonical name lookup?
+"""
+__docformat__ = 'epytext en'
+
+######################################################################
+## Imports
+######################################################################
+
+import types, re, os.path, pickle
+from epydoc import log
+import epydoc
+import __builtin__
+from epydoc.compat import * # Backwards compatibility
+from epydoc.util import decode_with_backslashreplace, py_src_filename
+import epydoc.markup.pyval_repr
+
+######################################################################
+# Dotted Names
+######################################################################
+
+class DottedName:
+ """
+ A sequence of identifiers, separated by periods, used to name a
+ Python variable, value, or argument. The identifiers that make up
+ a dotted name can be accessed using the indexing operator:
+
+ >>> name = DottedName('epydoc', 'api_doc', 'DottedName')
+ >>> print name
+ epydoc.apidoc.DottedName
+ >>> name[1]
+ 'api_doc'
+ """
+ UNREACHABLE = "??"
+ _IDENTIFIER_RE = re.compile("""(?x)
+ (%s | # UNREACHABLE marker, or..
+ (script-)? # Prefix: script (not a module)
+ \w+ # Identifier (yes, identifiers starting with a
+ # digit are allowed. See SF bug #1649347)
+ '?) # Suffix: submodule that is shadowed by a var
+ (-\d+)? # Suffix: unreachable vals with the same name
+ $"""
+ % re.escape(UNREACHABLE))
+
+ class InvalidDottedName(ValueError):
+ """
+ An exception raised by the DottedName constructor when one of
+ its arguments is not a valid dotted name.
+ """
+
+ _ok_identifiers = set()
+ """A cache of identifier strings that have been checked against
+ _IDENTIFIER_RE and found to be acceptable."""
+
+ def __init__(self, *pieces, **options):
+ """
+ Construct a new dotted name from the given sequence of pieces,
+ each of which can be either a C{string} or a C{DottedName}.
+ Each piece is divided into a sequence of identifiers, and
+ these sequences are combined together (in order) to form the
+ identifier sequence for the new C{DottedName}. If a piece
+ contains a string, then it is divided into substrings by
+ splitting on periods, and each substring is checked to see if
+ it is a valid identifier.
+
+ As an optimization, C{pieces} may also contain a single tuple
+ of values. In that case, that tuple will be used as the
+ C{DottedName}'s identifiers; it will I{not} be checked to
+ see if it's valid.
+
+ @kwparam strict: if true, then raise an L{InvalidDottedName}
+ if the given name is invalid.
+ """
+ if len(pieces) == 1 and isinstance(pieces[0], tuple):
+ self._identifiers = pieces[0] # Optimization
+ return
+ if len(pieces) == 0:
+ raise DottedName.InvalidDottedName('Empty DottedName')
+ self._identifiers = []
+ for piece in pieces:
+ if isinstance(piece, DottedName):
+ self._identifiers += piece._identifiers
+ elif isinstance(piece, basestring):
+ for subpiece in piece.split('.'):
+ if piece not in self._ok_identifiers:
+ if not self._IDENTIFIER_RE.match(subpiece):
+ if options.get('strict'):
+ raise DottedName.InvalidDottedName(
+ 'Bad identifier %r' % (piece,))
+ else:
+ log.warning("Identifier %r looks suspicious; "
+ "using it anyway." % piece)
+ self._ok_identifiers.add(piece)
+ self._identifiers.append(subpiece)
+ else:
+ raise TypeError('Bad identifier %r: expected '
+ 'DottedName or str' % (piece,))
+ self._identifiers = tuple(self._identifiers)
+
+ def __repr__(self):
+ idents = [`ident` for ident in self._identifiers]
+ return 'DottedName(' + ', '.join(idents) + ')'
+
+ def __str__(self):
+ """
+ Return the dotted name as a string formed by joining its
+ identifiers with periods:
+
+ >>> print DottedName('epydoc', 'api_doc', DottedName')
+ epydoc.apidoc.DottedName
+ """
+ return '.'.join(self._identifiers)
+
+ def __add__(self, other):
+ """
+ Return a new C{DottedName} whose identifier sequence is formed
+ by adding C{other}'s identifier sequence to C{self}'s.
+ """
+ if isinstance(other, (basestring, DottedName)):
+ return DottedName(self, other)
+ else:
+ 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:
+ return DottedName(*(list(other)+[self]))
+
+ def __getitem__(self, i):
+ """
+ Return the C{i}th identifier in this C{DottedName}. If C{i} is
+ 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]
+ if pieces: return DottedName(pieces)
+ else: return []
+ else:
+ return self._identifiers[i]
+
+ def __hash__(self):
+ return hash(self._identifiers)
+
+ def __cmp__(self, other):
+ """
+ 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
+ return cmp(self._identifiers, other._identifiers)
+
+ def __len__(self):
+ """
+ Return the number of identifiers in this dotted name.
+ """
+ return len(self._identifiers)
+
+ def container(self):
+ """
+ Return the DottedName formed by removing the last identifier
+ from this dotted name's identifier sequence. If this dotted
+ name only has one name in its identifier sequence, return
+ C{None} instead.
+ """
+ if len(self._identifiers) == 1:
+ return None
+ else:
+ 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
+ """
+ len_self = len(self._identifiers)
+ len_name = len(name._identifiers)
+
+ if (len_self > len_name) or (strict and len_self == len_name):
+ return False
+ # The following is redundant (the first clause is implied by
+ # the second), but is done as an optimization.
+ return ((self._identifiers[0] == name._identifiers[0]) and
+ self._identifiers == name._identifiers[:len_self])
+
+ def contextualize(self, context):
+ """
+ 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
+ if self[0] == context[0]:
+ return self[1:].contextualize(context[1:])
+ else:
+ return self
+
+ # Find the first index where self & context differ.
+ for i in range(min(len(context), len(self))):
+ if self._identifiers[i] != context._identifiers[i]:
+ first_difference = i
+ break
+ else:
+ first_difference = i+1
+
+ # Strip off anything before that index.
+ if first_difference == 0:
+ return self
+ elif first_difference == len(self):
+ return self[-1:]
+ else:
+ return self[first_difference:]
+
+######################################################################
+# UNKNOWN Value
+######################################################################
+
+class _Sentinel:
+ """
+ A unique value that won't compare equal to any other value. This
+ class is used to create L{UNKNOWN}.
+ """
+ def __init__(self, name):
+ self.name = name
+ def __repr__(self):
+ return '<%s>' % self.name
+ def __nonzero__(self):
+ raise ValueError('Sentinel value <%s> can not be used as a boolean' %
+ self.name)
+
+UNKNOWN = _Sentinel('UNKNOWN')
+"""A special value used to indicate that a given piece of
+information about an object is unknown. This is used as the
+default value for all instance variables."""
+
+######################################################################
+# API Documentation Objects: Abstract Base Classes
+######################################################################
+
+class APIDoc(object):
+ """
+ API documentation information for a single element of a Python
+ program. C{APIDoc} itself is an abstract base class; subclasses
+ are used to specify what information should be recorded about each
+ type of program element. In particular, C{APIDoc} has two direct
+ subclasses, C{VariableDoc} for documenting variables and
+ C{ValueDoc} for documenting values; and the C{ValueDoc} class is
+ subclassed further for different value types.
+
+ Each C{APIDoc} subclass specifies the set of attributes that
+ should be used to record information about the corresponding
+ program element type. The default value for each attribute is
+ stored in the class; these default values can then be overridden
+ with instance variables. Most attributes use the special value
+ L{UNKNOWN} as their default value, to indicate that the correct
+ value for that attribute has not yet been determined. This makes
+ it easier to merge two C{APIDoc} objects that are documenting the
+ same element (in particular, to merge information about an element
+ that was derived from parsing with information that was derived
+ from introspection).
+
+ For all attributes with boolean values, use only the constants
+ C{True} and C{False} to designate true and false. In particular,
+ do I{not} use other values that evaluate as true or false, such as
+ C{2} or C{()}. This restriction makes it easier to handle
+ C{UNKNOWN} values. For example, to test if a boolean attribute is
+ C{True} or C{UNKNOWN}, use 'C{attrib in (True, UNKNOWN)}' or
+ 'C{attrib is not False}'.
+
+ Two C{APIDoc} objects describing the same object can be X{merged},
+ using the method L{merge_and_overwrite(other)}. After two
+ C{APIDoc}s are merged, any changes to one will be reflected in the
+ other. This is accomplished by setting the two C{APIDoc} objects
+ to use a shared instance dictionary. See the documentation for
+ L{merge_and_overwrite} for more information, and some important
+ caveats about hashing.
+ """
+ #{ Docstrings
+ docstring = UNKNOWN
+ """@ivar: The documented item's docstring.
+ @type: C{string} or C{None}"""
+
+ docstring_lineno = UNKNOWN
+ """@ivar: The line number on which the documented item's docstring
+ begins.
+ @type: C{int}"""
+ #} end of "docstrings" group
+
+ #{ Information Extracted from Docstrings
+ descr = UNKNOWN
+ """@ivar: A description of the documented item, extracted from its
+ docstring.
+ @type: L{ParsedDocstring<epydoc.markup.ParsedDocstring>}"""
+
+ summary = UNKNOWN
+ """@ivar: A summary description of the documented item, extracted from
+ its docstring.
+ @type: L{ParsedDocstring<epydoc.markup.ParsedDocstring>}"""
+
+ other_docs = UNKNOWN
+ """@ivar: A flag indicating if the entire L{docstring} body (except tags
+ if any) is entirely included in the L{summary}.
+ @type: C{bool}"""
+
+ metadata = UNKNOWN
+ """@ivar: Metadata about the documented item, extracted from fields in
+ its docstring. I{Currently} this is encoded as a list of tuples
+ C{(field, arg, descr)}. But that may change.
+ @type: C{(str, str, L{ParsedDocstring<markup.ParsedDocstring>})}"""
+
+ extra_docstring_fields = UNKNOWN
+ """@ivar: A list of new docstring fields tags that are defined by the
+ documented item's docstring. These new field tags can be used by
+ this item or by any item it contains.
+ @type: L{DocstringField <epydoc.docstringparser.DocstringField>}"""
+ #} end of "information extracted from docstrings" group
+
+ #{ Source Information
+ docs_extracted_by = UNKNOWN # 'parser' or 'introspecter' or 'both'
+ """@ivar: Information about where the information contained by this
+ C{APIDoc} came from. Can be one of C{'parser'},
+ C{'introspector'}, or C{'both'}.
+ @type: C{str}"""
+ #} end of "source information" group
+
+ def __init__(self, **kwargs):
+ """
+ Construct a new C{APIDoc} object. Keyword arguments may be
+ used to initialize the new C{APIDoc}'s attributes.
+
+ @raise TypeError: If a keyword argument is specified that does
+ not correspond to a valid attribute for this (sub)class of
+ C{APIDoc}.
+ """
+ if epydoc.DEBUG:
+ for key in kwargs:
+ if key[0] != '_' and 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. 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}. (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.
+ if attr.startswith('_'):
+ return object.__setattr__(self, attr, val)
+ if not hasattr(self, attr):
+ raise AttributeError('%s does not define attribute %r' %
+ (self.__class__.__name__, attr))
+ self.__dict__[attr] = val
+
+ if epydoc.DEBUG:
+ __setattr__ = _debug_setattr
+
+ def __repr__(self):
+ return '<%s>' % self.__class__.__name__
+
+ def pp(self, doublespace=0, depth=5, exclude=(), include=()):
+ """
+ Return a pretty-printed string representation for the
+ information contained in this C{APIDoc}.
+ """
+ return pp_apidoc(self, doublespace, depth, exclude, include)
+ __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('Can not specialize to %r' % cls)
+ # Update the class.
+ self.__class__ = cls
+ # 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__)
+
+ __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__)
+
+ def __cmp__(self, other):
+ if not isinstance(other, APIDoc): return -1
+ if self.__dict__ is other.__dict__: return 0
+ name_cmp = cmp(self.canonical_name, other.canonical_name)
+ if name_cmp == 0: return -1
+ else: return name_cmp
+
+ def is_detailed(self):
+ """
+ Does this object deserve a box with extra details?
+
+ @return: True if the object needs extra details, else False.
+ @rtype: C{bool}
+ """
+ if self.other_docs is True:
+ return True
+
+ if self.metadata is not UNKNOWN:
+ return bool(self.metadata)
+
+ __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
+ that any changes made to one will affect the other. Any
+ attributes that C{other} had before merging will be discarded.
+ This is accomplished by copying C{self.__dict__} over
+ C{other.__dict__} and C{self.__class__} over C{other.__class__}.
+
+ Care must be taken with this method, since it modifies the
+ hash value of C{other}. To help avoid the problems that this
+ can cause, C{merge_and_overwrite} will raise an exception if
+ C{other} has ever been hashed, unless C{ignore_hash_conflict}
+ is True. Note that adding C{other} to a dictionary, set, or
+ similar data structure will implicitly cause it to be hashed.
+ If you do set C{ignore_hash_conflict} to True, then any
+ existing data structures that rely on C{other}'s hash staying
+ constant may become corrupted.
+
+ @return: C{self}
+ @raise ValueError: If C{other} has ever been hashed.
+ """
+ # If we're already merged, then there's nothing to do.
+ if (self.__dict__ is other.__dict__ and
+ self.__class__ is other.__class__): return self
+
+ if other.__has_been_hashed and not ignore_hash_conflict:
+ raise ValueError("%r has already been hashed! Merging it "
+ "would cause its hash value to change." % other)
+
+ # If other was itself already merged with anything,
+ # then we need to merge those too.
+ a,b = (self.__mergeset, other.__mergeset)
+ mergeset = (self.__mergeset or [self]) + (other.__mergeset or [other])
+ other.__dict__.clear()
+ for apidoc in mergeset:
+ #if apidoc is self: pass
+ apidoc.__class__ = self.__class__
+ apidoc.__dict__ = self.__dict__
+ self.__mergeset = mergeset
+ # Sanity chacks.
+ assert self in mergeset and other in mergeset
+ for apidoc in mergeset:
+ assert apidoc.__dict__ is self.__dict__
+ # Return self.
+ return self
+
+ def apidoc_links(self, **filters):
+ """
+ Return a list of all C{APIDoc}s that are directly linked from
+ this C{APIDoc} (i.e., are contained or pointed to by one or
+ more of this C{APIDoc}'s attributes.)
+
+ Keyword argument C{filters} can be used to selectively exclude
+ certain categories of attribute value. For example, using
+ C{includes=False} will exclude variables that were imported
+ from other modules; and C{subclasses=False} will exclude
+ subclasses. The filter categories currently supported by
+ epydoc are:
+ - C{imports}: Imported variables.
+ - C{packages}: Containing packages for modules.
+ - C{submodules}: Contained submodules for packages.
+ - C{bases}: Bases for classes.
+ - C{subclasses}: Subclasses for classes.
+ - C{variables}: All variables.
+ - C{private}: Private variables.
+ - C{overrides}: Points from class variables to the variables
+ they override. This filter is False by default.
+ """
+ return []
+
+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 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.
+ """
+ apidoc_queue = list(root)
+ val_set = set()
+ var_set = set()
+ while apidoc_queue:
+ api_doc = apidoc_queue.pop()
+ if isinstance(api_doc, ValueDoc):
+ val_set.add(api_doc)
+ else:
+ 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])
+ return val_set
+
+######################################################################
+# Variable Documentation Objects
+######################################################################
+
+class VariableDoc(APIDoc):
+ """
+ API documentation information about a single Python variable.
+
+ @note: The only time a C{VariableDoc} will have its own docstring
+ is if that variable was created using an assignment statement, and
+ that assignment statement had a docstring-comment or was followed
+ by a pseudo-docstring.
+ """
+ #{ Basic Variable Information
+ name = UNKNOWN
+ """@ivar: The name of this variable in its containing namespace.
+ @type: C{str}"""
+
+ container = UNKNOWN
+ """@ivar: API documentation for the namespace that contains this
+ variable.
+ @type: L{ValueDoc}"""
+
+ canonical_name = UNKNOWN
+ """@ivar: A dotted name that serves as a unique identifier for
+ this C{VariableDoc}. It should be formed by concatenating
+ the C{VariableDoc}'s C{container} with its C{name}.
+ @type: L{DottedName}"""
+
+ value = UNKNOWN
+ """@ivar: The API documentation for this variable's value.
+ @type: L{ValueDoc}"""
+ #}
+
+ #{ Information Extracted from Docstrings
+ type_descr = UNKNOWN
+ """@ivar: A description of the variable's expected type, extracted from
+ its docstring.
+ @type: L{ParsedDocstring<epydoc.markup.ParsedDocstring>}"""
+ #} end of "information extracted from docstrings" group
+
+ #{ Information about Imported Variables
+ imported_from = UNKNOWN
+ """@ivar: The fully qualified dotted name of the variable that this
+ variable's value was imported from. This attribute should only
+ be defined if C{is_instvar} is true.
+ @type: L{DottedName}"""
+
+ is_imported = UNKNOWN
+ """@ivar: Was this variable's value imported from another module?
+ (Exception: variables that are explicitly included in __all__ have
+ C{is_imported} set to C{False}, even if they are in fact
+ imported.)
+ @type: C{bool}"""
+ #} end of "information about imported variables" group
+
+ #{ Information about Variables in Classes
+ is_instvar = UNKNOWN
+ """@ivar: If true, then this variable is an instance variable; if false,
+ then this variable is a class variable. This attribute should
+ only be defined if the containing namespace is a class
+ @type: C{bool}"""
+
+ overrides = UNKNOWN # [XXX] rename -- don't use a verb.
+ """@ivar: The API documentation for the variable that is overridden by
+ this variable. This attribute should only be defined if the
+ containing namespace is a class.
+ @type: L{VariableDoc}"""
+ #} end of "information about variables in classes" group
+
+ #{ Flags
+ is_alias = UNKNOWN
+ """@ivar: Is this variable an alias for another variable with the same
+ value? If so, then this variable will be dispreferred when
+ assigning canonical names.
+ @type: C{bool}"""
+
+ is_public = UNKNOWN
+ """@ivar: Is this variable part of its container's public API?
+ @type: C{bool}"""
+ #} end of "flags" group
+
+ def __init__(self, **kwargs):
+ APIDoc.__init__(self, **kwargs)
+ if self.is_public is UNKNOWN and self.name is not UNKNOWN:
+ self.is_public = (not self.name.startswith('_') or
+ self.name.endswith('_'))
+
+ def __repr__(self):
+ if self.canonical_name is not UNKNOWN:
+ return '<%s %s>' % (self.__class__.__name__, self.canonical_name)
+ if self.name is not UNKNOWN:
+ return '<%s %s>' % (self.__class__.__name__, self.name)
+ else:
+ return '<%s>' % self.__class__.__name__
+
+ 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):
+ # nb: overrides filter is *False* by default.
+ if (filters.get('overrides', False) and
+ (self.overrides not in (None, UNKNOWN))):
+ overrides = [self.overrides]
+ else:
+ overrides = []
+ if self.value in (None, UNKNOWN):
+ return []+overrides
+ else:
+ return [self.value]+overrides
+
+ def is_detailed(self):
+ pval = super(VariableDoc, self).is_detailed()
+ if pval or self.value in (None, UNKNOWN):
+ return pval
+
+ if (self.overrides not in (None, UNKNOWN) and
+ isinstance(self.value, RoutineDoc)):
+ return True
+
+ if isinstance(self.value, GenericValueDoc):
+ # [XX] This is a little hackish -- we assume that the
+ # summary lines will have SUMMARY_REPR_LINELEN chars,
+ # that len(name) of those will be taken up by the name,
+ # and that 3 of those will be taken up by " = " between
+ # the name & val. Note that if any docwriter uses a
+ # different formula for maxlen for this, then it will
+ # not get the right value for is_detailed().
+ maxlen = self.value.SUMMARY_REPR_LINELEN-3-len(self.name)
+ return (not self.value.summary_pyval_repr(maxlen).is_complete)
+ else:
+ return self.value.is_detailed()
+
+######################################################################
+# Value Documentation Objects
+######################################################################
+
+class ValueDoc(APIDoc):
+ """
+ API documentation information about a single Python value.
+ """
+ canonical_name = UNKNOWN
+ """@ivar: A dotted name that serves as a unique identifier for
+ this C{ValueDoc}'s value. If the value can be reached using a
+ single sequence of identifiers (given the appropriate imports),
+ then that sequence of identifiers is used as its canonical name.
+ If the value can be reached by multiple sequences of identifiers
+ (i.e., if it has multiple aliases), then one of those sequences of
+ identifiers is used. If the value cannot be reached by any
+ sequence of identifiers (e.g., if it was used as a base class but
+ then its variable was deleted), then its canonical name will start
+ with C{'??'}. If necessary, a dash followed by a number will be
+ appended to the end of a non-reachable identifier to make its
+ canonical name unique.
+
+ When possible, canonical names are chosen when new C{ValueDoc}s
+ are created. However, this is sometimes not possible. If a
+ canonical name can not be chosen when the C{ValueDoc} is created,
+ then one will be assigned by L{assign_canonical_names()
+ <docbuilder.assign_canonical_names>}.
+
+ @type: L{DottedName}"""
+
+ #{ Value Representation
+ pyval = UNKNOWN
+ """@ivar: A pointer to the actual Python object described by this
+ C{ValueDoc}. This is used to display the value (e.g., when
+ describing a variable.) Use L{pyval_repr()} to generate a
+ plaintext string representation of this value.
+ @type: Python object"""
+
+ parse_repr = UNKNOWN
+ """@ivar: A text representation of this value, extracted from
+ parsing its source code. This representation may not accurately
+ reflect the actual value (e.g., if the value was modified after
+ the initial assignment).
+ @type: C{unicode}"""
+
+ REPR_MAXLINES = 5
+ """@cvar: The maximum number of lines of text that should be
+ generated by L{pyval_repr()}. If the string representation does
+ not fit in this number of lines, an ellpsis marker (...) will
+ be placed at the end of the formatted representation."""
+
+ REPR_LINELEN = 75
+ """@cvar: The maximum number of characters for lines of text that
+ should be generated by L{pyval_repr()}. Any lines that exceed
+ this number of characters will be line-wrappped; The S{crarr}
+ symbol will be used to indicate that the line was wrapped."""
+
+ SUMMARY_REPR_LINELEN = 75
+ """@cvar: The maximum number of characters for the single-line
+ text representation generated by L{summary_pyval_repr()}. If
+ the value's representation does not fit in this number of
+ characters, an ellipsis marker (...) will be placed at the end
+ of the formatted representation."""
+
+ REPR_MIN_SCORE = 0
+ """@cvar: The minimum score that a value representation based on
+ L{pyval} should have in order to be used instead of L{parse_repr}
+ as the canonical representation for this C{ValueDoc}'s value.
+ @see: L{epydoc.markup.pyval_repr}"""
+ #} end of "value representation" group
+
+ #{ Context
+ defining_module = UNKNOWN
+ """@ivar: The documentation for the module that defines this
+ value. This is used, e.g., to lookup the appropriate markup
+ language for docstrings. For a C{ModuleDoc},
+ C{defining_module} should be C{self}.
+ @type: L{ModuleDoc}"""
+ #} end of "context group"
+
+ #{ Information about Imported Variables
+ proxy_for = None # [xx] in progress.
+ """@ivar: If C{proxy_for} is not None, then this value was
+ imported from another file. C{proxy_for} is the dotted name of
+ the variable that this value was imported from. If that
+ variable is documented, then its C{value} may contain more
+ complete API documentation about this value. The C{proxy_for}
+ attribute is used by the source code parser to link imported
+ values to their source values (in particular, for base
+ classes). When possible, these proxy C{ValueDoc}s are replaced
+ by the imported value's C{ValueDoc} by
+ L{link_imports()<docbuilder.link_imports>}.
+ @type: L{DottedName}"""
+ #} end of "information about imported variables" group
+
+ #: @ivar:
+ #: This is currently used to extract values from __all__, etc, in
+ #: the docparser module; maybe I should specialize
+ #: process_assignment and extract it there? Although, for __all__,
+ #: it's not clear where I'd put the value, since I just use it to
+ #: set private/public/imported attribs on other vars (that might not
+ #: exist yet at the time.)
+ toktree = UNKNOWN
+
+ def __repr__(self):
+ if self.canonical_name is not UNKNOWN:
+ return '<%s %s>' % (self.__class__.__name__, self.canonical_name)
+ else:
+ return '<%s %s>' % (self.__class__.__name__,
+ self.summary_pyval_repr().to_plaintext(None))
+
+ def __setstate__(self, state):
+ self.__dict__ = state
+
+ def __getstate__(self):
+ """
+ State serializer for the pickle module. This is necessary
+ because sometimes the C{pyval} attribute contains an
+ un-pickleable value.
+ """
+ # Construct our pickled dictionary. Maintain this dictionary
+ # as a private attribute, so we can reuse it later, since
+ # merged objects need to share a single dictionary.
+ if not hasattr(self, '_ValueDoc__pickle_state'):
+ # Make sure __pyval_repr & __summary_pyval_repr are cached:
+ self.pyval_repr(), self.summary_pyval_repr()
+ # Construct the dictionary; leave out 'pyval'.
+ self.__pickle_state = self.__dict__.copy()
+ self.__pickle_state['pyval'] = UNKNOWN
+
+ if not isinstance(self, GenericValueDoc):
+ assert self.__pickle_state != {}
+ # Return the pickle state.
+ return self.__pickle_state
+
+ #{ Value Representation
+ def pyval_repr(self):
+ """
+ Return a formatted representation of the Python object
+ described by this C{ValueDoc}. This representation may
+ include data from introspection or parsing, and is authorative
+ as 'the best way to represent a Python value.' Any lines that
+ go beyond L{REPR_LINELEN} characters will be wrapped; and if
+ the representation as a whole takes more than L{REPR_MAXLINES}
+ lines, then it will be truncated (with an ellipsis marker).
+ This function will never return L{UNKNOWN} or C{None}.
+
+ @rtype: L{ColorizedPyvalRepr}
+ """
+ # Use self.__pyval_repr to cache the result.
+ if not hasattr(self, '_ValueDoc__pyval_repr'):
+ self.__pyval_repr = epydoc.markup.pyval_repr.colorize_pyval(
+ self.pyval, self.parse_repr, self.REPR_MIN_SCORE,
+ self.REPR_LINELEN, self.REPR_MAXLINES, linebreakok=True)
+ return self.__pyval_repr
+
+ def summary_pyval_repr(self, max_len=None):
+ """
+ Return a single-line formatted representation of the Python
+ object described by this C{ValueDoc}. This representation may
+ include data from introspection or parsing, and is authorative
+ as 'the best way to summarize a Python value.' If the
+ representation takes more then L{SUMMARY_REPR_LINELEN}
+ characters, then it will be truncated (with an ellipsis
+ marker). This function will never return L{UNKNOWN} or
+ C{None}.
+
+ @rtype: L{ColorizedPyvalRepr}
+ """
+ # If max_len is specified, then do *not* cache the result.
+ if max_len is not None:
+ return epydoc.markup.pyval_repr.colorize_pyval(
+ self.pyval, self.parse_repr, self.REPR_MIN_SCORE,
+ max_len, maxlines=1, linebreakok=False)
+
+ # Use self.__summary_pyval_repr to cache the result.
+ if not hasattr(self, '_ValueDoc__summary_pyval_repr'):
+ self.__summary_pyval_repr = epydoc.markup.pyval_repr.colorize_pyval(
+ self.pyval, self.parse_repr, self.REPR_MIN_SCORE,
+ self.SUMMARY_REPR_LINELEN, maxlines=1, linebreakok=False)
+ return self.__summary_pyval_repr
+ #} end of "value representation" group
+
+ def apidoc_links(self, **filters):
+ return []
+
+class GenericValueDoc(ValueDoc):
+ """
+ API documentation about a 'generic' value, i.e., one that does not
+ have its own docstring or any information other than its value and
+ parse representation. C{GenericValueDoc}s do not get assigned
+ cannonical names.
+ """
+ canonical_name = None
+
+ def is_detailed(self):
+ return (not self.summary_pyval_repr().is_complete)
+
+class NamespaceDoc(ValueDoc):
+ """
+ API documentation information about a singe Python namespace
+ value. (I.e., a module or a class).
+ """
+ #{ Information about Variables
+ variables = UNKNOWN
+ """@ivar: The contents of the namespace, encoded as a
+ dictionary mapping from identifiers to C{VariableDoc}s. This
+ dictionary contains all names defined by the namespace,
+ including imported variables, aliased variables, and variables
+ inherited from base classes (once L{inherit_docs()
+ <epydoc.docbuilder.inherit_docs>} has added them).
+ @type: C{dict} from C{string} to L{VariableDoc}"""
+ sorted_variables = UNKNOWN
+ """@ivar: A list of all variables defined by this
+ namespace, in sorted order. The elements of this list should
+ exactly match the values of L{variables}. The sort order for
+ this list is defined as follows:
+ - Any variables listed in a C{@sort} docstring field are
+ listed in the order given by that field.
+ - These are followed by any variables that were found while
+ parsing the source code, in the order in which they were
+ defined in the source file.
+ - Finally, any remaining variables are listed in
+ alphabetical order.
+ @type: C{list} of L{VariableDoc}"""
+ sort_spec = UNKNOWN
+ """@ivar: The order in which variables should be listed,
+ encoded as a list of names. Any variables whose names are not
+ included in this list should be listed alphabetically,
+ following the variables that are included.
+ @type: C{list} of C{str}"""
+ group_specs = UNKNOWN
+ """@ivar: The groups that are defined by this namespace's
+ docstrings. C{group_specs} is encoded as an ordered list of
+ tuples C{(group_name, elt_names)}, where C{group_name} is the
+
+ name of a group and C{elt_names} is a list of element names in
+ that group. (An element can be a variable or a submodule.) A
+ '*' in an element name will match any string of characters.
+ @type: C{list} of C{(str,list)}"""
+ variable_groups = UNKNOWN
+ """@ivar: A dictionary specifying what group each
+ variable belongs to. The keys of the dictionary are group
+ names, and the values are lists of C{VariableDoc}s. The order
+ that groups should be listed in should be taken from
+ L{group_specs}.
+ @type: C{dict} from C{str} to C{list} of L{VariableDoc}"""
+ #} end of group "information about variables"
+
+ def __init__(self, **kwargs):
+ kwargs.setdefault('variables', {})
+ APIDoc.__init__(self, **kwargs)
+ assert self.variables is not UNKNOWN
+
+ def is_detailed(self):
+ return True
+
+ def apidoc_links(self, **filters):
+ variables = filters.get('variables', True)
+ imports = filters.get('imports', True)
+ private = filters.get('private', True)
+ if variables and imports and private:
+ return self.variables.values() # list the common case first.
+ elif not variables:
+ return []
+ elif not imports and not private:
+ return [v for v in self.variables.values() if
+ v.is_imported != True and v.is_public != False]
+ elif not private:
+ return [v for v in self.variables.values() if
+ v.is_public != False]
+ elif not imports:
+ return [v for v in self.variables.values() if
+ v.is_imported != True]
+ assert 0, 'this line should be unreachable'
+
+ def init_sorted_variables(self):
+ """
+ Initialize the L{sorted_variables} attribute, based on the
+ L{variables} and L{sort_spec} attributes. This should usually
+ be called after all variables have been added to C{variables}
+ (including any inherited variables for classes).
+ """
+ unsorted = self.variables.copy()
+ self.sorted_variables = []
+
+ # Add any variables that are listed in sort_spec
+ if self.sort_spec is not UNKNOWN:
+ unused_idents = set(self.sort_spec)
+ for ident in self.sort_spec:
+ if ident in unsorted:
+ self.sorted_variables.append(unsorted.pop(ident))
+ unused_idents.discard(ident)
+ elif '*' in ident:
+ regexp = re.compile('^%s$' % ident.replace('*', '(.*)'))
+ # sort within matching group?
+ for name, var_doc in unsorted.items():
+ if regexp.match(name):
+ self.sorted_variables.append(unsorted.pop(name))
+ unused_idents.discard(ident)
+ for ident in unused_idents:
+ if ident not in ['__all__', '__docformat__', '__path__']:
+ log.warning("@sort: %s.%s not found" %
+ (self.canonical_name, ident))
+
+
+ # Add any remaining variables in alphabetical order.
+ var_docs = unsorted.items()
+ var_docs.sort()
+ for name, var_doc in var_docs:
+ self.sorted_variables.append(var_doc)
+
+ def init_variable_groups(self):
+ """
+ Initialize the L{variable_groups} attribute, based on the
+ L{sorted_variables} and L{group_specs} attributes.
+ """
+ if self.sorted_variables is UNKNOWN:
+ self.init_sorted_variables()
+ assert len(self.sorted_variables) == len(self.variables)
+
+ elts = [(v.name, v) for v in self.sorted_variables]
+ self._unused_groups = dict([(n,set(i)) for (n,i) in self.group_specs])
+ self.variable_groups = self._init_grouping(elts)
+
+ def group_names(self):
+ """
+ Return a list of the group names defined by this namespace, in
+ the order in which they should be listed, with no duplicates.
+ """
+ name_list = ['']
+ name_set = set()
+ for name, spec in self.group_specs:
+ if name not in name_set:
+ name_set.add(name)
+ name_list.append(name)
+ return name_list
+
+ def _init_grouping(self, elts):
+ """
+ Divide a given a list of APIDoc objects into groups, as
+ specified by L{self.group_specs}.
+
+ @param elts: A list of tuples C{(name, apidoc)}.
+
+ @return: A list of tuples C{(groupname, elts)}, where
+ C{groupname} is the name of a group and C{elts} is a list of
+ C{APIDoc}s in that group. The first tuple has name C{''}, and
+ is used for ungrouped elements. The remaining tuples are
+ listed in the order that they appear in C{self.group_specs}.
+ Within each tuple, the elements are listed in the order that
+ they appear in C{api_docs}.
+ """
+ # Make the common case fast.
+ if len(self.group_specs) == 0:
+ return {'': [elt[1] for elt in elts]}
+
+ ungrouped = set([elt_doc for (elt_name, elt_doc) in elts])
+
+ ungrouped = dict(elts)
+ groups = {}
+ for elt_name, elt_doc in elts:
+ for (group_name, idents) in self.group_specs:
+ group = groups.setdefault(group_name, [])
+ unused_groups = self._unused_groups[group_name]
+ for ident in idents:
+ if re.match('^%s$' % ident.replace('*', '(.*)'), elt_name):
+ unused_groups.discard(ident)
+ if elt_name in ungrouped:
+ group.append(ungrouped.pop(elt_name))
+ elif elt_name not in set(idents):
+ log.warning("%s.%s in multiple groups" %
+ (self.canonical_name, elt_name))
+
+ # Convert ungrouped from an unordered set to an ordered list.
+ groups[''] = [elt_doc for (elt_name, elt_doc) in elts
+ if elt_name in ungrouped]
+ return groups
+
+ def report_unused_groups(self):
+ """
+ Issue a warning for any @group items that were not used by
+ L{_init_grouping()}.
+ """
+ for (group, unused_idents) in self._unused_groups.items():
+ for ident in unused_idents:
+ log.warning("@group %s: %s.%s not found" %
+ (group, self.canonical_name, ident))
+
+class ModuleDoc(NamespaceDoc):
+ """
+ API documentation information about a single module.
+ """
+ #{ Information about the Module
+ filename = UNKNOWN
+ """@ivar: The name of the file that defines the module.
+ @type: C{string}"""
+ docformat = UNKNOWN
+ """@ivar: The markup language used by docstrings in this module.
+ @type: C{string}"""
+ #{ Information about Submodules
+ submodules = UNKNOWN
+ """@ivar: Modules contained by this module (if this module
+ is a package). (Note: on rare occasions, a module may have a
+ submodule that is shadowed by a variable with the same name.)
+ @type: C{list} of L{ModuleDoc}"""
+ submodule_groups = UNKNOWN
+ """@ivar: A dictionary specifying what group each
+ submodule belongs to. The keys of the dictionary are group
+ names, and the values are lists of C{ModuleDoc}s. The order
+ that groups should be listed in should be taken from
+ L{group_specs}.
+ @type: C{dict} from C{str} to C{list} of L{ModuleDoc}"""
+ #{ Information about Packages
+ package = UNKNOWN
+ """@ivar: API documentation for the module's containing package.
+ @type: L{ModuleDoc}"""
+ is_package = UNKNOWN
+ """@ivar: True if this C{ModuleDoc} describes a package.
+ @type: C{bool}"""
+ path = UNKNOWN
+ """@ivar: If this C{ModuleDoc} describes a package, then C{path}
+ contains a list of directories that constitute its path (i.e.,
+ the value of its C{__path__} variable).
+ @type: C{list} of C{str}"""
+ #{ Information about Imported Variables
+ imports = UNKNOWN
+ """@ivar: A list of the source names of variables imported into
+ this module. This is used to construct import graphs.
+ @type: C{list} of L{DottedName}"""
+ #}
+
+ def apidoc_links(self, **filters):
+ val_docs = NamespaceDoc.apidoc_links(self, **filters)
+ if (filters.get('packages', True) and
+ self.package not in (None, UNKNOWN)):
+ val_docs.append(self.package)
+ if (filters.get('submodules', True) and
+ self.submodules not in (None, UNKNOWN)):
+ val_docs += self.submodules
+ return val_docs
+
+ def init_submodule_groups(self):
+ """
+ Initialize the L{submodule_groups} attribute, based on the
+ L{submodules} and L{group_specs} attributes.
+ """
+ if self.submodules in (None, UNKNOWN):
+ return
+ self.submodules = sorted(self.submodules,
+ key=lambda m:m.canonical_name)
+ elts = [(m.canonical_name[-1], m) for m in self.submodules]
+ self.submodule_groups = self._init_grouping(elts)
+
+ def select_variables(self, group=None, value_type=None, public=None,
+ imported=None, detailed=None):
+ """
+ Return a specified subset of this module's L{sorted_variables}
+ list. If C{value_type} is given, then only return variables
+ whose values have the specified type. If C{group} is given,
+ then only return variables that belong to the specified group.
+
+ @require: The L{sorted_variables}, L{variable_groups}, and
+ L{submodule_groups} attributes must be initialized before
+ this method can be used. See L{init_sorted_variables()},
+ L{init_variable_groups()}, and L{init_submodule_groups()}.
+
+ @param value_type: A string specifying the value type for
+ which variables should be returned. Valid values are:
+ - 'class' - variables whose values are classes or types.
+ - 'function' - variables whose values are functions.
+ - 'other' - variables whose values are not classes,
+ exceptions, types, or functions.
+ @type value_type: C{string}
+
+ @param group: The name of the group for which variables should
+ be returned. A complete list of the groups defined by
+ this C{ModuleDoc} is available in the L{group_names}
+ instance variable. The first element of this list is
+ always the special group name C{''}, which is used for
+ variables that do not belong to any group.
+ @type group: C{string}
+
+ @param detailed: If True (False), return only the variables
+ deserving (not deserving) a detailed informative box.
+ If C{None}, don't care.
+ @type detailed: C{bool}
+ """
+ if (self.sorted_variables is UNKNOWN or
+ self.variable_groups is UNKNOWN):
+ raise ValueError('sorted_variables and variable_groups '
+ 'must be initialized first.')
+
+ if group is None: var_list = self.sorted_variables
+ else:
+ var_list = self.variable_groups.get(group, self.sorted_variables)
+
+ # Public/private filter (Count UNKNOWN as public)
+ if public is True:
+ var_list = [v for v in var_list if v.is_public is not False]
+ elif public is False:
+ var_list = [v for v in var_list if v.is_public is False]
+
+ # Imported filter (Count UNKNOWN as non-imported)
+ if imported is True:
+ var_list = [v for v in var_list if v.is_imported is True]
+ elif imported is False:
+ var_list = [v for v in var_list if v.is_imported is not True]
+
+ # Detailed filter
+ if detailed is True:
+ var_list = [v for v in var_list if v.is_detailed() is True]
+ elif detailed is False:
+ var_list = [v for v in var_list if v.is_detailed() is not True]
+
+ # [xx] Modules are not currently included in any of these
+ # value types.
+ if value_type is None:
+ return var_list
+ elif value_type == 'class':
+ return [var_doc for var_doc in var_list
+ if (isinstance(var_doc.value, ClassDoc))]
+ elif value_type == 'function':
+ return [var_doc for var_doc in var_list
+ if isinstance(var_doc.value, RoutineDoc)]
+ elif value_type == 'other':
+ return [var_doc for var_doc in var_list
+ if not isinstance(var_doc.value,
+ (ClassDoc, RoutineDoc, ModuleDoc))]
+ else:
+ raise ValueError('Bad value type %r' % value_type)
+
+class ClassDoc(NamespaceDoc):
+ """
+ API documentation information about a single class.
+ """
+ #{ Information about Base Classes
+ bases = UNKNOWN
+ """@ivar: API documentation for the class's base classes.
+ @type: C{list} of L{ClassDoc}"""
+ #{ Information about Subclasses
+ subclasses = UNKNOWN
+ """@ivar: API documentation for the class's known subclasses.
+ @type: C{list} of L{ClassDoc}"""
+ #}
+ #{ Information about Metaclasses
+ metaclass = UNKNOWN
+ #}
+
+ def apidoc_links(self, **filters):
+ val_docs = NamespaceDoc.apidoc_links(self, **filters)
+ if (filters.get('bases', True) and
+ self.bases not in (None, UNKNOWN)):
+ val_docs += self.bases
+ if (filters.get('subclasses', True) and
+ self.subclasses not in (None, UNKNOWN)):
+ val_docs += self.subclasses
+ return val_docs
+
+ def is_type(self):
+ if self.canonical_name == DottedName('type'): return True
+ if self.bases is UNKNOWN: return False
+ for base in self.bases:
+ if isinstance(base, ClassDoc) and base.is_type():
+ return True
+ return False
+
+ def is_exception(self):
+ if self.canonical_name == DottedName('Exception'): return True
+ if self.bases is UNKNOWN: return False
+ for base in self.bases:
+ if isinstance(base, ClassDoc) and base.is_exception():
+ return True
+ return False
+
+ def is_newstyle_class(self):
+ if self.canonical_name == DottedName('object'): return True
+ if self.bases is UNKNOWN: return False
+ for base in self.bases:
+ if isinstance(base, ClassDoc) and base.is_newstyle_class():
+ return True
+ return False
+
+ def mro(self, warn_about_bad_bases=False):
+ if self.is_newstyle_class():
+ try:
+ return self._c3_mro(warn_about_bad_bases)
+ except ValueError, e: # (inconsistent hierarchy)
+ log.error('Error finding mro for %s: %s' %
+ (self.canonical_name, e))
+ # Better than nothing:
+ return self._dfs_bases([], set(), warn_about_bad_bases)
+ else:
+ return self._dfs_bases([], set(), warn_about_bad_bases)
+
+ def _dfs_bases(self, mro, seen, warn_about_bad_bases):
+ if self in seen: return mro
+ mro.append(self)
+ seen.add(self)
+ if self.bases is not UNKNOWN:
+ for base in self.bases:
+ if isinstance(base, ClassDoc) and base.proxy_for is None:
+ base._dfs_bases(mro, seen, warn_about_bad_bases)
+ elif warn_about_bad_bases:
+ self._report_bad_base(base)
+ return mro
+
+ def _c3_mro(self, warn_about_bad_bases):
+ """
+ Compute the class precedence list (mro) according to C3.
+ @seealso: U{http://www.python.org/2.3/mro.html}
+ """
+ bases = [base for base in self.bases if isinstance(base, ClassDoc)]
+ if len(bases) != len(self.bases) and warn_about_bad_bases:
+ for base in self.bases:
+ if (not isinstance(base, ClassDoc) or
+ base.proxy_for is not None):
+ self._report_bad_base(base)
+ w = [warn_about_bad_bases]*len(bases)
+ return self._c3_merge([[self]] + map(ClassDoc._c3_mro, bases, w) +
+ [list(bases)])
+
+ def _report_bad_base(self, base):
+ if not isinstance(base, ClassDoc):
+ if not isinstance(base, GenericValueDoc):
+ base_name = base.canonical_name
+ elif base.parse_repr is not UNKNOWN:
+ base_name = base.parse_repr
+ else:
+ base_name = '%r' % base
+ log.warning("%s's base %s is not a class" %
+ (self.canonical_name, base_name))
+ elif base.proxy_for is not None:
+ log.warning("No information available for %s's base %s" %
+ (self.canonical_name, base.proxy_for))
+
+ def _c3_merge(self, seqs):
+ """
+ Helper function for L{_c3_mro}.
+ """
+ res = []
+ while 1:
+ nonemptyseqs=[seq for seq in seqs if seq]
+ if not nonemptyseqs: return res
+ for seq in nonemptyseqs: # find merge candidates among seq heads
+ cand = seq[0]
+ nothead=[s for s in nonemptyseqs if cand in s[1:]]
+ if nothead: cand=None #reject candidate
+ else: break
+ if not cand: raise ValueError("Inconsistent hierarchy")
+ res.append(cand)
+ for seq in nonemptyseqs: # remove cand
+ if seq[0] == cand: del seq[0]
+
+ def select_variables(self, group=None, value_type=None, inherited=None,
+ public=None, imported=None, detailed=None):
+ """
+ Return a specified subset of this class's L{sorted_variables}
+ list. If C{value_type} is given, then only return variables
+ whose values have the specified type. If C{group} is given,
+ then only return variables that belong to the specified group.
+ If C{inherited} is True, then only return inherited variables;
+ if C{inherited} is False, then only return local variables.
+
+ @require: The L{sorted_variables} and L{variable_groups}
+ attributes must be initialized before this method can be
+ used. See L{init_sorted_variables()} and
+ L{init_variable_groups()}.
+
+ @param value_type: A string specifying the value type for
+ which variables should be returned. Valid values are:
+ - 'instancemethod' - variables whose values are
+ instance methods.
+ - 'classmethod' - variables whose values are class
+ methods.
+ - 'staticmethod' - variables whose values are static
+ methods.
+ - 'properties' - variables whose values are properties.
+ - 'class' - variables whose values are nested classes
+ (including exceptions and types).
+ - 'instancevariable' - instance variables. This includes
+ any variables that are explicitly marked as instance
+ variables with docstring fields; and variables with
+ docstrings that are initialized in the constructor.
+ - 'classvariable' - class variables. This includes any
+ variables that are not included in any of the above
+ categories.
+ @type value_type: C{string}
+
+ @param group: The name of the group for which variables should
+ be returned. A complete list of the groups defined by
+ this C{ClassDoc} is available in the L{group_names}
+ instance variable. The first element of this list is
+ always the special group name C{''}, which is used for
+ variables that do not belong to any group.
+ @type group: C{string}
+
+ @param inherited: If C{None}, then return both inherited and
+ local variables; if C{True}, then return only inherited
+ variables; if C{False}, then return only local variables.
+
+ @param detailed: If True (False), return only the variables
+ deserving (not deserving) a detailed informative box.
+ If C{None}, don't care.
+ @type detailed: C{bool}
+ """
+ if (self.sorted_variables is UNKNOWN or
+ self.variable_groups is UNKNOWN):
+ raise ValueError('sorted_variables and variable_groups '
+ 'must be initialized first.')
+
+ if group is None: var_list = self.sorted_variables
+ else: var_list = self.variable_groups[group]
+
+ # Public/private filter (Count UNKNOWN as public)
+ if public is True:
+ var_list = [v for v in var_list if v.is_public is not False]
+ elif public is False:
+ var_list = [v for v in var_list if v.is_public is False]
+
+ # Inherited filter (Count UNKNOWN as non-inherited)
+ if inherited is None: pass
+ elif inherited:
+ var_list = [v for v in var_list if v.container != self]
+ else:
+ var_list = [v for v in var_list if v.container == self ]
+
+ # Imported filter (Count UNKNOWN as non-imported)
+ if imported is True:
+ var_list = [v for v in var_list if v.is_imported is True]
+ elif imported is False:
+ var_list = [v for v in var_list if v.is_imported is not True]
+
+ # Detailed filter
+ if detailed is True:
+ var_list = [v for v in var_list if v.is_detailed() is True]
+ elif detailed is False:
+ var_list = [v for v in var_list if v.is_detailed() is not True]
+
+ if value_type is None:
+ return var_list
+ elif value_type == 'method':
+ return [var_doc for var_doc in var_list
+ if (isinstance(var_doc.value, RoutineDoc) and
+ var_doc.is_instvar in (False, UNKNOWN))]
+ elif value_type == 'instancemethod':
+ return [var_doc for var_doc in var_list
+ if (isinstance(var_doc.value, RoutineDoc) and
+ not isinstance(var_doc.value, ClassMethodDoc) and
+ not isinstance(var_doc.value, StaticMethodDoc) and
+ var_doc.is_instvar in (False, UNKNOWN))]
+ elif value_type == 'classmethod':
+ return [var_doc for var_doc in var_list
+ if (isinstance(var_doc.value, ClassMethodDoc) and
+ var_doc.is_instvar in (False, UNKNOWN))]
+ elif value_type == 'staticmethod':
+ return [var_doc for var_doc in var_list
+ if (isinstance(var_doc.value, StaticMethodDoc) and
+ var_doc.is_instvar in (False, UNKNOWN))]
+ elif value_type == 'property':
+ return [var_doc for var_doc in var_list
+ if (isinstance(var_doc.value, PropertyDoc) and
+ var_doc.is_instvar in (False, UNKNOWN))]
+ elif value_type == 'class':
+ return [var_doc for var_doc in var_list
+ if (isinstance(var_doc.value, ClassDoc) and
+ var_doc.is_instvar in (False, UNKNOWN))]
+ elif value_type == 'instancevariable':
+ return [var_doc for var_doc in var_list
+ if var_doc.is_instvar is True]
+ elif value_type == 'classvariable':
+ return [var_doc for var_doc in var_list
+ if (var_doc.is_instvar in (False, UNKNOWN) and
+ not isinstance(var_doc.value,
+ (RoutineDoc, ClassDoc, PropertyDoc)))]
+ else:
+ raise ValueError('Bad value type %r' % value_type)
+
+class RoutineDoc(ValueDoc):
+ """
+ API documentation information about a single routine.
+ """
+ #{ Signature
+ posargs = UNKNOWN
+ """@ivar: The names of the routine's positional arguments.
+ If an argument list contains \"unpacking\" arguments, then
+ their names will be specified using nested lists. E.g., if
+ a function's argument list is C{((x1,y1), (x2,y2))}, then
+ posargs will be C{[['x1','y1'], ['x2','y2']]}.
+ @type: C{list}"""
+ posarg_defaults = UNKNOWN
+ """@ivar: API documentation for the positional arguments'
+ default values. This list has the same length as C{posargs}, and
+ each element of C{posarg_defaults} describes the corresponding
+ argument in C{posargs}. For positional arguments with no default,
+ C{posargs_defaults} will contain None.
+ @type: C{list} of C{ValueDoc} or C{None}"""
+ vararg = UNKNOWN
+ """@ivar: The name of the routine's vararg argument, or C{None} if
+ it has no vararg argument.
+ @type: C{string} or C{None}"""
+ kwarg = UNKNOWN
+ """@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
+
+ #{ Decorators
+ decorators = UNKNOWN
+ """@ivar: A list of names of decorators that were applied to this
+ routine, in the order that they are listed in the source code.
+ (I.e., in the reverse of the order that they were applied in.)
+ @type: C{list} of C{string}"""
+ #} end of "decorators" group
+
+ #{ Information Extracted from Docstrings
+ arg_descrs = UNKNOWN
+ """@ivar: A list of descriptions of the routine's
+ arguments. Each element of this list is a tuple C{(args,
+ descr)}, where C{args} is a list of argument names; and
+ C{descr} is a L{ParsedDocstring
+ <epydoc.markup.ParsedDocstring>} describing the argument(s)
+ specified by C{arg}.
+ @type: C{list}"""
+ arg_types = UNKNOWN
+ """@ivar: Descriptions of the expected types for the
+ routine's arguments, encoded as a dictionary mapping from
+ argument names to type descriptions.
+ @type: C{dict} from C{string} to L{ParsedDocstring
+ <epydoc.markup.ParsedDocstring>}"""
+ return_descr = UNKNOWN
+ """@ivar: A description of the value returned by this routine.
+ @type: L{ParsedDocstring<epydoc.markup.ParsedDocstring>}"""
+ return_type = UNKNOWN
+ """@ivar: A description of expected type for the value
+ returned by this routine.
+ @type: L{ParsedDocstring<epydoc.markup.ParsedDocstring>}"""
+ exception_descrs = UNKNOWN
+ """@ivar: A list of descriptions of exceptions
+ that the routine might raise. Each element of this list is a
+ tuple C{(exc, descr)}, where C{exc} is a string contianing the
+ exception name; and C{descr} is a L{ParsedDocstring
+ <epydoc.markup.ParsedDocstring>} describing the circumstances
+ under which the exception specified by C{exc} is raised.
+ @type: C{list}"""
+ #} end of "information extracted from docstrings" group
+ callgraph_uid = None
+ """@ivar: L{DotGraph}.uid of the call graph for the function.
+ @type: C{str}"""
+
+ def is_detailed(self):
+ if super(RoutineDoc, self).is_detailed():
+ return True
+
+ if self.arg_descrs not in (None, UNKNOWN) and self.arg_descrs:
+ return True
+
+ if self.arg_types not in (None, UNKNOWN) and self.arg_types:
+ return True
+
+ if self.return_descr not in (None, UNKNOWN):
+ return True
+
+ if self.exception_descrs not in (None, UNKNOWN) and self.exception_descrs:
+ return True
+
+ if (self.decorators not in (None, UNKNOWN)
+ and [ d for d in self.decorators
+ if d not in ('classmethod', 'staticmethod') ]):
+ return True
+
+ return False
+
+ def all_args(self):
+ """
+ @return: A list of the names of all arguments (positional,
+ vararg, and keyword), in order. If a positional argument
+ consists of a tuple of names, then that tuple will be
+ flattened.
+ """
+ if self.posargs is UNKNOWN:
+ return UNKNOWN
+
+ all_args = _flatten(self.posargs)
+ if self.vararg not in (None, UNKNOWN):
+ all_args.append(self.vararg)
+ if self.kwarg not in (None, UNKNOWN):
+ all_args.append(self.kwarg)
+ return all_args
+
+def _flatten(lst, out=None):
+ """
+ Return a flattened version of C{lst}.
+ """
+ if out is None: out = []
+ for elt in lst:
+ if isinstance(elt, (list,tuple)):
+ _flatten(elt, out)
+ else:
+ out.append(elt)
+ return out
+
+class ClassMethodDoc(RoutineDoc): pass
+class StaticMethodDoc(RoutineDoc): pass
+
+class PropertyDoc(ValueDoc):
+ """
+ API documentation information about a single property.
+ """
+ #{ Property Access Functions
+ fget = UNKNOWN
+ """@ivar: API documentation for the property's get function.
+ @type: L{RoutineDoc}"""
+ fset = UNKNOWN
+ """@ivar: API documentation for the property's set function.
+ @type: L{RoutineDoc}"""
+ fdel = UNKNOWN
+ """@ivar: API documentation for the property's delete function.
+ @type: L{RoutineDoc}"""
+ #}
+ #{ Information Extracted from Docstrings
+ type_descr = UNKNOWN
+ """@ivar: A description of the property's expected type, extracted
+ from its docstring.
+ @type: L{ParsedDocstring<epydoc.markup.ParsedDocstring>}"""
+ #} end of "information extracted from docstrings" group
+
+ def apidoc_links(self, **filters):
+ val_docs = []
+ if self.fget not in (None, UNKNOWN): val_docs.append(self.fget)
+ if self.fset not in (None, UNKNOWN): val_docs.append(self.fset)
+ if self.fdel not in (None, UNKNOWN): val_docs.append(self.fdel)
+ return val_docs
+
+ def is_detailed(self):
+ if super(PropertyDoc, self).is_detailed():
+ return True
+
+ if self.fget not in (None, UNKNOWN) and self.fget.pyval is not None:
+ return True
+ if self.fset not in (None, UNKNOWN) and self.fset.pyval is not None:
+ return True
+ if self.fdel not in (None, UNKNOWN) and self.fdel.pyval is not None:
+ return True
+
+ return False
+
+######################################################################
+## Index
+######################################################################
+
+class DocIndex:
+ """
+ [xx] out of date.
+
+ An index that .. hmm... it *can't* be used to access some things,
+ cuz they're not at the root level. Do I want to add them or what?
+ And if so, then I have a sort of a new top level. hmm.. so
+ basically the question is what to do with a name that's not in the
+ root var's name space. 2 types:
+ - entirely outside (eg os.path)
+ - inside but not known (eg a submodule that we didn't look at?)
+ - container of current thing not examined?
+
+ An index of all the C{APIDoc} objects that can be reached from a
+ root set of C{ValueDoc}s.
+
+ The members of this index can be accessed by dotted name. In
+ particular, C{DocIndex} defines two mappings, accessed via the
+ L{get_vardoc()} and L{get_valdoc()} methods, which can be used to
+ access C{VariableDoc}s or C{ValueDoc}s respectively by name. (Two
+ separate mappings are necessary because a single name can be used
+ to refer to both a variable and to the value contained by that
+ variable.)
+
+ Additionally, the index defines two sets of C{ValueDoc}s:
+ \"reachable C{ValueDoc}s\" and \"contained C{ValueDoc}s\". The
+ X{reachable C{ValueDoc}s} are defined as the set of all
+ C{ValueDoc}s that can be reached from the root set by following
+ I{any} sequence of pointers to C{ValueDoc}s or C{VariableDoc}s.
+ The X{contained C{ValueDoc}s} are defined as the set of all
+ C{ValueDoc}s that can be reached from the root set by following
+ only the C{ValueDoc} pointers defined by non-imported
+ C{VariableDoc}s. For example, if the root set contains a module
+ C{m}, then the contained C{ValueDoc}s includes the C{ValueDoc}s
+ for any functions, variables, or classes defined in that module,
+ as well as methods and variables defined in classes defined in the
+ module. The reachable C{ValueDoc}s includes all of those
+ C{ValueDoc}s, as well as C{ValueDoc}s for any values imported into
+ the module, and base classes for classes defined in the module.
+ """
+
+ def __init__(self, root):
+ """
+ Create a new documentation index, based on the given root set
+ of C{ValueDoc}s. If any C{APIDoc}s reachable from the root
+ set does not have a canonical name, then it will be assigned
+ one. etc.
+
+ @param root: A list of C{ValueDoc}s.
+ """
+ for apidoc in root:
+ if apidoc.canonical_name in (None, UNKNOWN):
+ raise ValueError("All APIdocs passed to DocIndexer "
+ "must already have canonical names.")
+
+ # Initialize the root items list. We sort them by length in
+ # ascending order. (This ensures that variables will shadow
+ # submodules when appropriate.)
+ # When the elements name is the same, list in alphabetical order:
+ # this is needed by the check for duplicates below.
+ self.root = sorted(root,
+ key=lambda d: (len(d.canonical_name), d.canonical_name))
+ """The list of C{ValueDoc}s to document.
+ @type: C{list}"""
+
+ # Drop duplicated modules
+ # [xx] maybe what causes duplicates should be fixed instead.
+ # If fixed, adjust the sort here above: sorting by names will not
+ # be required anymore
+ i = 1
+ while i < len(self.root):
+ if self.root[i-1] is self.root[i]:
+ del self.root[i]
+ else:
+ i += 1
+
+ self.mlclasses = self._get_module_classes(self.root)
+ """A mapping from class names to L{ClassDoc}. Contains
+ classes defined at module level for modules in L{root}
+ and which can be used as fallback by L{find()} if looking
+ in containing namespaces fails.
+ @type: C{dict} from C{str} to L{ClassDoc} or C{list}"""
+
+ 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}"""
+
+ self._funcid_to_doc = {}
+ """A mapping from C{profile} function ids to corresponding
+ C{APIDoc} objects. A function id is a tuple of the form
+ C{(filename, lineno, funcname)}. This is used to update
+ the L{callers} and L{callees} variables."""
+
+ self._container_cache = {}
+ """A cache for the L{container()} method, to increase speed."""
+
+ self._get_cache = {}
+ """A cache for the L{get_vardoc()} and L{get_valdoc()} methods,
+ to increase speed."""
+
+ #////////////////////////////////////////////////////////////
+ # Lookup methods
+ #////////////////////////////////////////////////////////////
+ # [xx]
+ # Currently these only work for things reachable from the
+ # root... :-/ I might want to change this so that imported
+ # values can be accessed even if they're not contained.
+ # Also, I might want canonical names to not start with ??
+ # if the thing is a top-level imported module..?
+
+ def get_vardoc(self, name):
+ """
+ Return the C{VariableDoc} with the given name, or C{None} if this
+ index does not contain a C{VariableDoc} with the given name.
+ """
+ var, val = self._get(name)
+ return var
+
+ def get_valdoc(self, name):
+ """
+ Return the C{ValueDoc} with the given name, or C{None} if this
+ index does not contain a C{ValueDoc} with the given name.
+ """
+ var, val = self._get(name)
+ return val
+
+ def _get(self, name):
+ """
+ A helper function that's used to implement L{get_vardoc()}
+ and L{get_valdoc()}.
+ """
+ # Convert name to a DottedName, if necessary.
+ if not isinstance(name, DottedName):
+ name = DottedName(name)
+
+ # Check if the result is cached.
+ val = self._get_cache.get(name)
+ if val is not None: return val
+
+ # Look for an element in the root set whose name is a prefix
+ # of `name`. If we can't find one, then return None.
+ for root_valdoc in self.root:
+ if root_valdoc.canonical_name.dominates(name):
+ # Starting at the root valdoc, walk down the variable/
+ # submodule chain until we find the requested item.
+ var_doc = None
+ val_doc = root_valdoc
+ for identifier in name[len(root_valdoc.canonical_name):]:
+ if val_doc is None: break
+ var_doc, val_doc = self._get_from(val_doc, identifier)
+ else:
+ # If we found it, then return.
+ if var_doc is not None or val_doc is not None:
+ self._get_cache[name] = (var_doc, val_doc)
+ return var_doc, val_doc