[Xml-logilab] inheritance/generalisation parent is None?

Sylvain Thénault Sylvain Thénault
Wed, 25 Sep 2002 11:02:52 +0200


--phCU5ROyZO6kBE05
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: inline
Content-Transfer-Encoding: 8bit

On Monday 23 September à 20:09, abulka@netspace.net.au wrote:
> Hi Sylvain,
>  Thanks for the new script - it worked and installed ok.
>  I ran successfully on my 
> 
> class A:
>     pass
> class B(A):
>     pass
> 
> example and also on the full reversing of the Pyreverse itself (as illustrated 
> in the readme).
> 
> In both cases there were no errors, however the bug is still there, namely, the 
> generalisation entries as viewed in argo/poseidon still read
>   B extends
> rather than
>   B extends A
> 
> Can you confirm that my Class A class B trivial example works for you?  I 
> cannot understand why this most basic revsing does not produce correct 
> results?  I would expect to be able to drag the classes A and B onto an argo 
> diagram and see them auto join up with a inheritance/generalisation arrow.
> 
> If I create the generalisation manually, in argo, I do get the correct 
> behaviour, as described.

It was a bug which is now fixed :-) You can fix it by replacing the file
visitor/inspector.py by the one joined to this mail, or by downloading
the 0.4.1 release which fix this bug and the installer problem

regards
-- 
Sylvain Thénault

  LOGILAB           http://www.logilab.org


--phCU5ROyZO6kBE05
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="inspector.py"

# Copyright (c) 2000-2002 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
"""
visitors that do some postprocessing on the tree generated by the parser,
like trying to resolve definitions (namespace) dictionnary, relationship...
"""

__revision__ = "$Id: inspector.py,v 1.2 2002/09/25 08:54:36 syt Exp $"

from logilab.pyreverse.extensions.visitor import FilteredVisitor
from logilab.pyreverse.base.objects import AbstractBase, Klass, EXCEPTION

import re
CALL = re.compile('^(\w|\.)+\((\w|,)*\)')

class NamespaceInspector(FilteredVisitor):
    """
    try to associate each value in the context dict with a reference to an
    object
    """

    def visit_project(self, node):
        _defs = node._defs
        # add child modules defs to _defs:
        for child in node.children:
            for key, val in child._defs.items():
                fname = child.fullname()
                if not _defs.has_key('%s.%s'%(fname,key)):
                    _defs['%s.%s'%(fname,key)] = val
                if fname != child.name:
                    _defs[fname] = child

    def visit_module(self, node):
        self._resolve_defs(node)
    
    def visit_class(self, node):
        self._resolve_defs(node)

    def visit_function(self, node):
        # if parent is a class, remove first parameter (self) ?
        if node.children and isinstance(node.parent, Klass):
            del node.children[0]
        self._resolve_defs(node)
    
    def visit_attribute(self, node):
        pass

    def visit_parameter(self, node):
        pass

    # protected methods ########################################################

    def _resolve_defs(self, node):
        _defs = node._defs
        # resolve definitions graph
        for key, val in _defs.items():
            if not isinstance(val, AbstractBase):
                defs = node.get_ref(val)
                if defs:
                    _defs[key] = defs


class AttributeInspector(FilteredVisitor):
    """
    walk on the project tree and resolve some attributes according to the context
    try to resolve: _ klass.parents
                    _ attribute.default
    """
    
    def visit_project(self, node):
        pass

    def visit_module(self, node):
        # resolve imports
        self._resolve_imports(node)
        # resolve exceptions
        self._resolve_exceptions(node)
    
    def visit_class(self, node):
        # resolve imports
        self._resolve_imports(node)
        # resolve exceptions
        self._resolve_exceptions(node)
        # resolve ancestors
        if node.parents:
            parents_l = []
            for parent in node.parents:
                parent = full_get_ref(node.get_ref, Klass, parent)
                parents_l.append(parent)
                if type(parent) == type(''):
                    if EXCEPTION.match(parent):
                        node.signal = 'true'
                elif parent.signal == 'true':
                    node.signal = 'true'
                    parent.subclasses.append(node)
                else:
                    parent.subclasses.append(node)
            node.parents = parents_l

    def visit_function(self, node):
        # resolve imports
        self._resolve_imports(node)
        # resolve exceptions
        self._resolve_exceptions(node)
        
    def visit_attribute(self, node):
        """
        try to get an object reference from the attribute default value
        """
        if hasattr(node, 'default'):
            defv = node.default
            if CALL.match(defv):
                node.default = full_get_ref(node.parent.get_ref, AbstractBase,
                                            defv[:defv.index('(')])
    
    def visit_parameter(self, node):
        pass

    # protected methods ########################################################
    
    def _resolve_imports(self, node):
        """
        try to get an object reference for imports
        """
        imp = []
        for mod, asmod in node.imports:
            if mod[-2:] == '.*':
                mod = mod[:-2]
            val = full_get_ref(node.get_ref, AbstractBase, mod)
            imp.append((val, asmod))
        node.imports = imp
        
    def _resolve_exceptions(self, node):
        """
        try to get an object reference for exception instances
        """
        raises_l = []
        for exception in node.raises:
            if CALL.match(exception):
                exception = full_get_ref(node.get_ref, Klass,
                                         exception[:exception.index('(')])
                if isinstance(exception, AbstractBase):
                    if not hasattr(exception, 'context'):
                        exception.context = []
                    exception.context.append(node)
            raises_l.append(exception)
        node.raises = raises_l


def full_get_ref(get_ref_func, base, name, warn=0):
    assert not isinstance(name, AbstractBase)
    refs = get_ref_func(name)
    if refs is not None and isinstance(refs, base):
        return refs
    elif refs is not None and refs!= name:
        done = [name]
        while refs and not isinstance(refs, base) and not refs in done:
            done.append(refs)
            last = refs
            refs = get_ref_func(refs)
        if refs:
            return refs
        else:
            return last
    if warn:
        import sys
        print >>sys.stderr, "Unresolved name %s" % name
    return name



--phCU5ROyZO6kBE05--