Exemple #1
0
def resolve_reference(ref, rel):
    """Looks up an interrogate symbol to its canonical name.  The second
    argument is the fully qualified name it should be seen relative to, which
    may be a module name, or a module name followed by an object name.

    If found, returns a 2-tuple (type, fqname), else None."""

    # Find out which module we should be looking in.
    modname = None
    relpath = None
    rel_parts = rel.split('.')
    for i in range(len(rel_parts), 0, -1):
        try_modname = '.'.join(rel_parts[:i])
        if idb.has_module(try_modname):
            modname = try_modname
            relpath = rel_parts[i:]
            break

    if not modname:
        return None

    refpath = ref.replace('::', '.').split('.')

    # Say `rel` is "panda3d.core.NodePath.node",
    # and `ref` is "PandaNode.final", then we will try these in this order:
    # - panda3d.core::NodePath.node.PandaNode.final
    # - panda3d.core::NodePath.PandaNode.final
    # - panda3d.core::PandaNode.final

    for i in range(len(relpath), -1, -1):
        search = relpath[:i] + refpath
        ifunc = idb.lookup_function(modname, search)
        if ifunc:
            # Grab the mangled function name.
            func_name = idb.get_function_name(ifunc, mangle=True)
            return ('meth', '.'.join(relpath[:i] + refpath[:-1] + [func_name]))

        itype = idb.lookup_type(modname, search)
        if itype:
            # Grab the original type name.
            type_name = idb.get_type_name(itype, mangle=False, scoped=True)
            return ('class', type_name)
Exemple #2
0
def on_missing_reference(app, env, node, contnode):
    # Resolver for interrogate classes that supports either snake case or camel
    # case naming.  Depending on the variation that is active, it will link to
    # either the Python or C++ reference as appropriate.

    target = node['reftarget']

    # Figure out which part is the module and which part is the class.
    prefix = ''
    module = 'panda3d.core'
    if target.startswith('panda3d.'):
        parts = target.split('.', 2)
        if len(parts) == 2:
            # It's trying to resolve a reference to a module; we can't help
            # with that.
            return

        module = '.'.join(parts[:2])
        prefix = module + '.'
        target = '.'.join(parts[2:])
    else:
        # Something like .core.NodePath, perhaps?
        modpart = target.split('.', 1)[0]
        if idb.has_module('panda3d.' + modpart):
            module = 'panda3d.' + modpart
            prefix = modpart + '.'
            target = target.split('.', 1)[1]

    variation = getattr(env.app.builder, 'current_variation', None)
    if variation and variation[0] == 'cpp':
        domain = env.domains['cpp']
    else:
        domain = env.domains['py']

    resolved = target and resolve_reference(target, module, domain=domain.name)

    typ = node['reftype']
    if domain.name == 'cpp' and typ == 'meth':
        # C++ domain doesn't have "meth", everything is "func" there.
        typ = 'func'

    if resolved and (resolved[0] == typ or typ == 'obj'):
        refdoc = node.get('refdoc', env.docname)

        # Try to match the original, but with the canonical mangling
        # (depending on Python versus C++)
        if len(contnode.children) and not node.get('refexplicit'):
            oldtext = contnode.children[0].astext()

            if domain.name == 'cpp':
                text = resolved[1]
                text = '::'.join(
                    text.split('::')[-oldtext.replace('.', '::').count('.') -
                                     1:])
            else:
                text = prefix + resolved[1]
                text = '.'.join(text.split('.')[-oldtext.count('.') - 1:])

            if oldtext.endswith("()"):
                text += "()"

            contnode.children[0] = nodes.Text(text)
        elif domain.name == 'cpp':
            # Work around a bug in the C++ resolver, which expects this
            # text node to be the child of an Element.  I picked a
            # decoration element since it happens not to translate to
            # anything (not sure what its purpose is).
            if isinstance(contnode, nodes.Text):
                contnode = nodes.decoration('', contnode)

        # C++ references don't have a module prefix and use :: for scoping
        if domain.name == 'cpp':
            target = resolved[1]
            if typ == 'obj':
                # Another bug workaround
                typ = resolved[0]
            if typ in ('enum', 'class', 'struct',
                       'union') and resolved[0] == 'type':
                # Squelch warning
                typ = resolved[0]
        else:
            target = prefix + resolved[1]

        return domain.resolve_xref(env, refdoc, app.builder, typ, target, node,
                                   contnode)
Exemple #3
0
def resolve_reference(ref, rel, domain='py'):
    """Looks up an interrogate symbol to its canonical name.  The second
    argument is the fully qualified name it should be seen relative to, which
    may be a module name, or a module name followed by an object name.

    If found, returns a 2-tuple (type, fqname), else None."""

    # Find out which module we should be looking in.
    modname = None
    relpath = None
    rel_parts = rel.replace('::', '.').split('.')
    for i in range(len(rel_parts), 0, -1):
        try_modname = '.'.join(rel_parts[:i])
        if idb.has_module(try_modname):
            modname = try_modname
            relpath = rel_parts[i:]
            break

    if not modname:
        return None

    refpath = ref.replace('::', '.').split('.')

    # Say `rel` is "panda3d.core.NodePath.node",
    # and `ref` is "PandaNode.final", then we will try these in this order:
    # - panda3d.core::NodePath.node.PandaNode.final
    # - panda3d.core::NodePath.PandaNode.final
    # - panda3d.core::PandaNode.final

    for i in range(len(relpath), -1, -1):
        search = relpath[:i] + refpath

        if len(refpath) == 1 and i > 0 and refpath[0] == relpath[i - 1]:
            # If we are looking for a name equal to the parent scope, we are
            # probably referencing a class name from within that very class.
            # We don't want to find the constructor, so skip this.
            continue

        ifunc = idb.lookup_function(modname, search)
        if ifunc:
            if domain == 'cpp':
                func_name = interrogate_function_scoped_name(ifunc)
                return ('func', func_name)
            elif domain == 'py':
                # Grab the mangled function name.
                func_name = idb.get_function_name(ifunc, mangle=True)
                if len(search) == 1:
                    return ('func', func_name)
                else:
                    return ('meth',
                            '.'.join(relpath[:i] + refpath[:-1] + [func_name]))

        itype = idb.lookup_type(modname, search)
        if itype:
            # Grab the original type name.
            if domain == 'cpp':
                type_name = interrogate_type_scoped_name(itype)
                if interrogate_type_is_typedef(itype):
                    return ('type', type_name)
                elif interrogate_type_is_enum(itype):
                    return ('enum', type_name)
                elif interrogate_type_is_struct(itype):
                    return ('struct', type_name)
                elif interrogate_type_is_class(itype):
                    return ('class', type_name)
                elif interrogate_type_is_union(itype):
                    return ('union', type_name)
            elif domain == 'py':
                type_name = idb.get_type_name(itype, mangle=False, scoped=True)
                return ('class', type_name)

    if len(rel_parts
           ) >= 2 and rel_parts[0] == 'panda3d' and rel_parts[1] != 'core':
        # Look in panda3d.core instead, prefix the result with the module name.
        rel_parts[1] = 'core'
        resolved = resolve_reference(ref, '.'.join(rel_parts), domain=domain)
        if resolved and domain == 'py':
            return (resolved[0], 'panda3d.core.' + resolved[1])
        else:
            return resolved