Search code examples
python-2.xpyramidbuildout

Can future's absolute_import clear __package__?


I have a Python 2 Pyramid App that is setup using buildout and a project folder activated using Mr.Developer; I am trying to futurize this project as part of Python 3 migration which changes:

import test

to:

from __future__ import absolute_import
from . import test

However for some reason ./bin/pserve development.ini doesn't come up, it says:

...
  File "/apps/src/project/engine/config.py", line 3, in <module>
    import utilities
  File "/apps/src/project/engine/utilities.py", line 9, in <module>
    from project.engine import spreadsheets
  File "/apps/src/project/engine/spreadsheets.py", line 16, in <module>
    from project.engine import utilities
ImportError: cannot import name utilities

It is a proper package as far as I am aware because it does have a setup.py and I ran develop activate project + buildout again.

If I type print(__package__) in that code it prints fine, but if I add a line from __future__ import absolute_import it prints None. Is it possible that future's absolute_import can simply clears out the __package__ variable and that is why it is not detecting this as a package?


This is the MWE of the real issue: Why does this circular import fail in Python 2 but not in Python 3?, it seems to be a bug in Python 2 when there is circular import. If anyone has a clean solution please let me know.


Solution

  • When __package__ is set to None, that's just a flag value for not yet set. It is not a problem.

    From PEP 366 – Main module explicit relative imports, on the subject of __package__:

    When the import system encounters an explicit relative import in a module without __package__ set (or with it set to None), it will calculate and store the correct value (__name__.rpartition('.')[0] for normal modules and __name__ for package initialisation modules). If __package__ has already been set then the import system will use it in preference to recalculating the package name from the __name__ and __path__ attributes.

    If you see this set to None in the main module and it was meant to be part of a package, then set it yourself:

    if __name__ == "__main__" and __package__ is None:
        __package__ = "foo.bar.baz"