[Python-projects] Check imported modules attributes

amaury.forgeotdarc at ubitrade.com amaury.forgeotdarc at ubitrade.com
Mon Jun 20 13:40:47 CEST 2005


Hello,

I just downloaded PyLint this morning, and tried it on my code.
This tool is really impressive... Nice job!
My first impression is that I have a lot of work to get a note that is not
negative...

Then, I tried to make it detect some errors in my code,
and I was surprised that PyLint doesn't detect simple (IMO)
errors like

    import sys
    sys.this_is_an_error


So I hacked a bit, and came with an additional test, that checks
attribute access: for each GetAttr node, if the left-hand side
is a module, the right-hand name must belong to the module.

Here are my mods to the checkers package:
(BTW, PyLint's code is very clear, and easy to modify. Bravo again!)

[in pylint/checkers/variable.py, at the end of the MSGS dict:]
    'E0623': ('Unresolved name %r in module %r',
              'Used when a name cannot be found in a module.'),

[in pylint/checkers/variable.py, class VariablesChecker:]

    def leave_getattr(self, node):
        """check modules attribute accesses"""
        # This function is a "leave_" because when parsing 'a.b.c'
        # we want to check the innermost expression first.

        module_names = []
        expr = node.expr

        # In the case of "package.module.name", retrieve the base package
        while isinstance(expr, astng.Getattr):
            module_names.insert(0, expr.attrname)
            expr = expr.expr
        if not isinstance(expr, astng.Name):
            # Base expression is not a Name, don't check
            return
        module_names.insert(0, expr.name)

        module = node
        for name in module_names:
            try:
                module = module.resolve(name)
            except astng.ResolveError:
                # This error is either a unknown variable,
                # or a missing module attribute. Both cases
                # are already reported.
                return
            if not isinstance(module, astng.Module):
                # Not a module, don't check
                return

        # locate the attribute inside the module
        try:
            module.resolve(node.attrname)
        except astng.ResolveError:
            module_name = '.'.join(module_names)
            self.add_message('E0623', args=(node.attrname, module_name),
node=node)



OK, this is straightforward and almost works.
The difficult part is that the tests relies on the imported modules
to be parsed  correctly. When using my new test on PyLint's code itself,
I saw it report errors on missing "sys.stdout" and "re.finditer"...

The first one was easy to find: when building the astng from a living
module,
functions, classes, and descriptors are taken, but not "regular" variables.
Here is a quick hack:

[in logilab/common/astng/builder.py, at the end of object_build:]
            else:
                # All other objects: just give a name
                from compiler.ast import EmptyNode
                data = EmptyNode() # XXX is it the right node?
                data.name = name
                # Keeping a reference to any object may have unexpected
results...
                # data.value = member
                node.add_local_node(data)

For "re.finditer", I don't have any solution yet. The function is defined
like this:
[in sre.py:]
    if sys.hexversion >= 0x02020000:
        def finditer(pattern, string):
            ...
An idea would be to import the module instead of trying to parse it.
I know this was removed some releases ago, but I suggest that at least
standard library modules could be "really" imported.


Well, sorry for this long post.
But I think that this kind of checks would avoid a lot of errors, specially
when imported modules are to change...

Thanks again for this tool,

--
Amaury Forgeot d'Arc
Ubix Development
www.ubitrade.com




More information about the Python-Projects mailing list