Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Circular imports hell

Python is extremely elegant language. Well, except... except imports. I still can't get it work the way it seems natural to me.

I have a class MyObjectA which is in file mypackage/myobjecta.py. This object uses some utility functions which are in mypackage/utils.py. So in my first lines in myobjecta.py I write:

from mypackage.utils import util_func1, util_func2

But some of the utility functions create and return new instances of MyObjectA. So I need to write in utils.py:

from mypackage.myobjecta import MyObjectA

Well, no I can't. This is a circular import and Python will refuse to do that.

There are many question here regarding this issue, but none seems to give satisfactory answer. From what I can read in all the answers:

  1. Reorganize your modules, you are doing it wrong! But I do not know how better to organize my modules even in such a simple case as I presented.
  2. Try just import ... rather than from ... import ... (personally I hate to write and potentially refactor all the full name qualifiers; I love to see what exactly I am importing into module from the outside world). Would that help? I am not sure, still there are circular imports.
  3. Do hacks like import something in the inner scope of a function body just one line before you use something from other module.

I am still hoping there is solution number 4) which would be Pythonic in the sense of being functional and elegant and simple and working. Or is there not?

Note: I am primarily a C++ programmer, the example above is so much easily solved by including corresponding headers that I can't believe it is not possible in Python.

like image 345
V.K. Avatar asked Nov 13 '15 11:11

V.K.


1 Answers

There is nothing hackish about importing something in a function body, it's an absolutely valid pattern:

def some_function():
    import logging
    do_some_logging()

Usually ImportErrors are only raised because of the way import() evaluates top level statements of the entire file when called.

In case you do not have a logic circular dependency... , nothing is impossible in python...

There is a way around it if you positively want your imports on top:

From David Beazleys excellent talk Modules and Packages: Live and Let Die! - PyCon 2015, 1:54:00, here is a way to deal with circular imports in python:

try:
    from images.serializers import SimplifiedImageSerializer
except ImportError:
    import sys
    SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']

This tries to import SimplifiedImageSerializer and if ImportError is raised (due to a circular import error or the it not existing) it will pull it from the importcache.

PS: You have to read this entire post in David Beazley's voice.

like image 109
Sebastian Wozny Avatar answered Sep 27 '22 17:09

Sebastian Wozny