Ticket #11108: trac_11108-classgraph-nt.patch

File trac_11108-classgraph-nt.patch, 6.8 KB (added by nthiery, 11 years ago)

Final patch with Nathann's review

  • doc/en/reference/misc.rst

    # HG changeset patch
    # User Nicolas M. Thiery <nthiery@users.sf.net>
    # Date 1301908614 -7200
    # Node ID de1035f2de5e8001ce7d025e316b755f2aed9d21
    # Parent  edc11cc6d0d2b0ee43ad6ee5875a4dcbf338bcbc
    #11108: improve class_graph
    * * *
    trac 11108 - improve classgraph (3-lines comment in the code)
    
    diff --git a/doc/en/reference/misc.rst b/doc/en/reference/misc.rst
    a b Miscellaneous 
    88
    99   sage/misc/abstract_method
    1010   sage/misc/cachefunc
     11   sage/misc/classgraph
    1112   sage/misc/function_mangling
    1213   sage/misc/exceptions
    1314   sage/misc/misc
  • sage/misc/classgraph.py

    diff --git a/sage/misc/classgraph.py b/sage/misc/classgraph.py
    a b  
     1r"""
     2Class inheritance graphs
     3"""
     4#*****************************************************************************
     5#  Copyright (C) 2007 William Stein <wstein@math.ucsd.edu>
     6#                2011 Nicolas M. Thiery <nthiery at users.sf.net>
     7#
     8#  Distributed under the terms of the GNU General Public License (GPL)
     9#                  http://www.gnu.org/licenses/
     10#******************************************************************************
     11
    112import inspect
    213
    3 def class_graph(top_module, depth=5, name_filter=None, classes=None):
     14def class_graph(top, depth=5, name_filter=None, classes=None, as_graph = True):
    415    """
    5     Return a dictionary with class names as keys and lists of class
    6     names as values. The values represent direct superclasses of the
    7     keys.
     16    Returns the class inheritance graph of a module, class, or object
    817
    9     This object is understood by e.g. the Graph and DiGraph constructors and
    10     can thus be used to create an inheritance graph for the given module.
    11    
    1218    INPUT:
    13         top_module -- the module to start in (e.g. sage)
    14         depth -- maximal recursion depth (default: 5)
    15         name_filter -- e.g. 'sage.rings' to only consider classes in sage.rings
    16         classes -- optional dictionary to be filled in (it is also returned)
    1719
    18     EXAMPLE:
     20     - ``top`` -- the module, class, or object to start with (e.g. ``sage``, ``Integer``, ``3``)
     21     - ``depth`` -- maximal recursion depth within submodules (default: 5)
     22     - ``name_filter`` -- e.g. 'sage.rings' to only consider classes in :mod:`sage.rings`
     23     - ``classes`` -- optional dictionary to be filled in (it is also returned)
     24     - ``as_graph`` -- a boolean (default: True)
     25
     26    OUTPUT:
     27
     28     - An oriented graph, with class names as vertices, and an edge
     29       from each class to each of its bases.
     30
     31    EXAMPLES:
     32
     33    We construct the inheritance graph of the classes within a given module::
     34
    1935        sage: from sage.rings.polynomial.padics import polynomial_padic_capped_relative_dense, polynomial_padic_flat
    20         sage: C = class_graph(sage.rings.polynomial.padics, depth=2)
    21         sage: C
     36        sage: G = class_graph(sage.rings.polynomial.padics); G
     37        Digraph on 4 vertices
     38        sage: G.vertices()
     39        ['Polynomial_generic_dense', 'Polynomial_generic_domain', 'Polynomial_padic_capped_relative_dense', 'Polynomial_padic_flat']
     40        sage: G.edges(labels=False)
     41        [('Polynomial_padic_capped_relative_dense', 'Polynomial_generic_domain'), ('Polynomial_padic_flat', 'Polynomial_generic_dense')]
     42
     43    We construct the inheritance graph of a given class::
     44
     45        sage: class_graph(Parent).edges(labels=False)
     46        [('CategoryObject', 'SageObject'), ('Parent', 'CategoryObject'), ('SageObject', 'object')]
     47
     48    We construct the inheritance graph of the class of an object::
     49
     50        sage: class_graph([1,2,3]).edges(labels=False)
     51        [('list', 'object')]
     52
     53    .. warning:: the output of ``class_graph`` used to be a dictionary
     54       mapping each class name to the list of names of its bases. This
     55       can be emulated by setting the option ``as_graph`` to ``False``::
     56
     57        sage: class_graph(sage.rings.polynomial.padics, depth=2, as_graph=False)
    2258        {'Polynomial_padic_capped_relative_dense': ['Polynomial_generic_domain'],
    2359        'Polynomial_padic_flat': ['Polynomial_generic_dense']}
    24         sage: Graph(C)
    25         Graph on 4 vertices
    26         sage: DiGraph(C)
     60
     61
     62    .. note:: the ``classes`` and ``as_graph`` options are mostly
     63       intended for internal recursive use.
     64
     65    .. note:: ``class_graph`` does not yet handle nested classes
     66
     67    TESTS::
     68
     69        sage: G = class_graph(sage.rings.polynomial.padics, depth=2); G
    2770        Digraph on 4 vertices
    2871    """
    29     # termination
    30     if depth == 0: return
     72    # This function descends recursively down the submodules of the
     73    # top module (if ``top`` is a module) and then down the hierarchy
     74    # of classes. Along the way, the result is stored in the "global"
     75    # dictionary ``classes`` which associates to each class the list
     76    # of its bases.
    3177
    32     if classes is None: classes = dict()
    33    
    34     if inspect.ismodule(top_module):
    35         if name_filter is None: name_filter = top_module.__name__
    36         top_module = top_module.__dict__.values()
    37    
    38     for X in top_module:
    39         if inspect.ismodule(X):
    40             if '.all' in X.__name__:
    41                 continue
    42             class_graph(X.__dict__.values(), depth=depth-1, name_filter=name_filter, classes=classes)
    43         if inspect.isclass(X):
    44             B = X.__bases__
    45             if len(B) > 0:
    46                 if X.__module__.startswith(name_filter):
    47                     classes[X.__name__] = [e.__name__ for e in B]
    48                 class_graph(B, depth=depth, name_filter=name_filter, classes=classes)
    49     return classes
     78    # Termination
     79    if depth < 0:
     80        return classes
     81
     82    # (first recursive call)
     83    if classes is None:
     84        classes = dict()
     85
     86    # Build the list ``children`` of submodules (resp. base classes)
     87    # of ``top`` the function will recurse through
     88    if inspect.ismodule(top):
     89        if top.__name__.endswith('.all'): # Ignore sage.rings.all and friends
     90            return classes
     91        if name_filter is None:
     92            name_filter = top.__name__
     93        children = [item for item in top.__dict__.values()
     94                       if inspect.ismodule(item) or inspect.isclass(item)]
     95        depth = depth -1
     96    elif inspect.isclass(top):
     97        if name_filter is None:
     98            name_filter = ""
     99        if not top.__module__.startswith(name_filter):
     100            return classes
     101        children = top.__bases__
     102        classes[top.__name__] = [e.__name__ for e in children]
     103    else: # top is a plain Python object; inspect its class
     104        children = [top.__class__]
     105
     106    # Recurse
     107    for child in children:
     108        class_graph(child, depth = depth, name_filter=name_filter, classes=classes, as_graph = False)
     109
     110    # (first recursive call): construct the graph
     111    if as_graph:
     112        from sage.graphs.digraph import DiGraph
     113        return DiGraph(classes)
     114    else:
     115        return classes
     116