Cyclic imports

Chris Angelico rosuav at gmail.com
Tue Aug 17 14:19:51 EDT 2021


On Wed, Aug 18, 2021 at 4:10 AM Barry Scott <barry at barrys-emacs.org> wrote:
>
>     def allImports( self, module_name ):
>             for line in f:
>                 words = line.strip().split()
>                 if words[0:1] == ['import']:
>                     all_imports.append( words[1] )
>

This will work for a lot of programs, but it depends too much on
coding style. If you feel like trying something a little more
adventurous, I'd recommend looking into the ast module:

>>> import ast
>>> ast.parse("""
... import foo
... import bar, baz
... from quux import spam
... try: import hello
... except ImportError: import goodbye
... """)
<ast.Module object at 0x7f83b65398d0>
>>> m = _
>>> ast.dump(m)
"Module(body=[Import(names=[alias(name='foo')]),
Import(names=[alias(name='bar'), alias(name='baz')]),
ImportFrom(module='quux', names=[alias(name='spam')], level=0),
Try(body=[Import(names=[alias(name='hello')])],
handlers=[ExceptHandler(type=Name(id='ImportError', ctx=Load()),
body=[Import(names=[alias(name='goodbye')])])], orelse=[],
finalbody=[])], type_ignores=[])"

If you ast.parse() the text of a Python script, you'll get a Module
that has all the top-level code in a list. It's then up to you how
much you dig into that. For instance, a simple try/except like this is
fairly common, but if something's inside a FunctionDef, you might want
to ignore it. Or maybe just ignore everything that isn't an Import or
FromImport, which would be a lot easier, but would miss the try/except
example.

The main advantage of ast.parse() is that it no longer cares about
code layout, and it won't be fooled by an import statement inside a
docstring, or anything like that. It's also pretty easy to handle
multiple variants (note how "import bar, baz" just has two things in
the names list).

ChrisA


More information about the Python-list mailing list