13

I've simplified my import problems down to this simple base case. Say I have a Python package:

mypkg/
   __init__.py
   a.py
   b.py

a.py contains:

def echo(msg):
    return msg

b.py contains:

from mypkg import a       # possibility 1, doesn't work
#import a                 # possibility 2, works
#from mypkg.a import echo  # import also fails

print(a.echo())

Running python b.py produces ImportError: No module named mypkg on both Python 2.7.6 and Python 3.3.5. I have also tried adding from __future__ import absolute_import in both cases, same issue.

Expected:

I expect possibility 1 to work just fine.

Why do I want to do this:

Possibility 2 is less desirable. Hypothetically, the standard library could introduce a package called a (unlikely in this case, but you get the idea). While Python 2 searches the current package first, Python 3+ includes absolute import changes so that the standard library is checked first. No matter what my reason, possibility 1 is supposed to work, no? I could swear I've done it thousands of times before.

Note: If you write a script external to mypkg, from mypkg import a works without issue.

My question is similar to python - absolute import for module in the same directory, but the author implies that what I have should be working.

Community
  • 1
  • 1
fractalous
  • 349
  • 2
  • 4
  • 10
  • The absolute path to the same folder is not needed. See the [answer](http://stackoverflow.com/a/11952026/2886003) to the cited question. – llrs May 08 '14 at 08:27
  • related: [how to use relative import within python spyder IDE](http://stackoverflow.com/q/23177615/4279) (see the links in my answer: they apply to the absolute imports too) – jfs May 08 '14 at 11:11

4 Answers4

22

from mypkg import a is the correct form. Don't run scripts from inside the Python package directory, it makes the same module available using multiple names that may lead to bugs. Run python -m mypkg.b from the directory that contains mypkg instead.

To be able to run from any directory, mypkg should be in pythonpath.

fractalous
  • 349
  • 2
  • 4
  • 10
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • `python -m mypkg.b` would also work fine if the import was written as a relative import, as in `from . import a` - and this to me seems more elegant as renaming mypkg won't break the import - but seems like relative imports are frowned upon. What is your opinion on this? – Mr_and_Mrs_D Nov 29 '22 at 14:04
  • 1
    @Mr_and_Mrs_D python scripts (ˋ__name__ == '__main__'ˋ) has to use absolute imports. – jfs Nov 30 '22 at 07:05
  • Aha makes sense - could you provide me with a refernce where this is discussed? – Mr_and_Mrs_D Nov 30 '22 at 12:18
  • 1
    @Mr_and_Mrs_D "modules intended for use as the main module of a Python application must always use absolute imports." https://docs.python.org/3.12/tutorial/modules.html#intra-package-references Though, it does not apply to the case in question: I do NOT get `ImportError: attempted relative import with no known parent package` with `python -m pkg.script` form, and it is supported by the docs: "This [relative] import style can be used when referencing modules within a package." https://docs.python.org/3.12/library/__main__.html#main-py-in-python-packages – jfs Nov 30 '22 at 18:44
2

Yes it will not work, because at the moment you call print(mypkg.a.echo()), mypkg is still loading (mypkg.__init__ -> mypkg.b). This is because Python loads parent modules first. https://docs.python.org/3/reference/import.html#searching

What you can do is wrap print(mypkg.a.echo()) into a function:

def echo():
   mypkg.a.echo()

And then:

import mypkg.b
mypkg.b.echo()

Or even:

print(sys.modules['mypkg.a'].echo())

Also you can help Python to find your module:

import importlib
mypkg.a = importlib.import_module('mypkg.a')
mypkg.a.echo()
excitoon
  • 328
  • 3
  • 11
0

I think the problem comes from the fact that you don't have a reference to mypkg inside the mypkg folder. Look at what Python is doing when I try to run your example (using the verbose option):

# trying /vagrant/mypkg/mypkg.py

That's why it can find the module, because it doesn't exist. One hack you could do is to have a file called mypkg.py with the line

import a

but that's just your 2nd possibility above in another jacket. Without knowing what you want to accomplish, I would choose the first example on the Intra-package Reference text. I would write b.py as such:

from a import echo

print(echo('message'))
perrocontodo
  • 113
  • 9
-1

Try this:

import sys
import os
this_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.dirname(this_dir))

from mypkg import a
print(a.echo())
fred.yu
  • 865
  • 7
  • 10
  • This *works*, but is frowned upon. If it's your own script and you're not sharing it, this is fine. But you don't want to be making packages that mess with users' `$PATH` environment variables. – Mike Williamson Jan 26 '19 at 03:06