SurpriseDog
SurpriseDog

Reputation: 484

Make flake8 differentiate between undefined functions and star imports

I have a rather large project that I'm trying to clean up before posting, but when I run flake8 I get tons of

'F405 <function> may be undefined, or defined from star imports: generic_functions`

I could avoid this by replacing the line:

from generic_functions import *

at the start of my file, but:

  1. I literally use all of the functions in there, so I don't understand how it's more pythonic to exceed the 80 character limit typing out every single function:

     from generic_functions import (function1, function2, function3, function4, function5, function6, function7...)
    
  2. Doing the above would be tedious, especially if I need to add or remove from the dozens of functions in generic_functions.py

The other option would be to disable the F405 warning, but what if the function is truly undefined? It would be nice to allow star imports while still catching anything undefined. Is there a way to do this? I tried adding # noqa to the import line, but it doesn't seem to help.

Upvotes: 4

Views: 4283

Answers (2)

SurpriseDog
SurpriseDog

Reputation: 484

You can use pylint to determine which functions are required and which ones are not. I wrote a function to automate this process. It works as follows:

  1. Run pylint on a script and match all of the W0614: Unused import errors.
  2. Get the list of variables in the target module with vars()
  3. Take the difference of those sets and only return things that can be imported.

import subprocess, types

def scrape_wildcard(filename, modvars):
    "Get variables imported from module in wild * import"
    error = "W0614: Unused import "
    unused = []
    for line in quickrun(['pylint', filename]):
        if error in line:
            unused.append(line.split(error)[1].split()[0])

    out = dict()
    for name in set(modvars.keys()) - set(unused):
        if not name.startswith('__'):
            func = modvars[name]
            if not isinstance(func, types.ModuleType):
                out[name] = modvars[name]
    return out


def quickrun(cmd, check=False, encoding='utf-8', errors='replace'):
    ret = subprocess.run(cmd, stdout=subprocess.PIPE, check=check)
    return ret.stdout.decode(encoding=encoding, errors=errors).splitlines()


import ????? as mymod
filename = "?????"
print('from', mymod.__name__, 'import', ', '.join(scrape_wildcard(filename, vars(mymod)).keys()))
'''

Replace the ????? with your module name and the filename of the module, respectively.

Upvotes: 0

sanyassh
sanyassh

Reputation: 8550

You can do the following:

  1. Leave your import as from generic_functions import *
  2. Run flake8 as flake8 --ignore=F405 file.py. I really don't know why # noqa doesn't work, but --ignore does.
  3. Test the rest of possible errors with pylint. Pylint is able to determine that <function> is defined and imported from generic_functions.

Upvotes: 5

Related Questions