5

I'm confused on why a simple absolute import is failing. Following the Python packages tutorial, I have package with a single subpackage:

sound/
    __init__.py
    top.py
    formats/
        __init__.py
        a.py
        b.py

a.py contains:

def foo():
    print("foo")

b.py contains:

from a import foo

def bar():
    foo()

if __name__ == "__main__":
    bar()

top.py contains:

from formats import b    

if __name__ == "__main__":
    b.bar()

Both __init__.py files are empty. From sound/formats/, running b prints foo as expected. But from sound/, running top produces the error:

File ".../sound/top.py", line 1, in <module>
  from formats import b
File "...\sound\format\b.py", line 1, in <module>
  from a import foo
ImportError: No module named 'a'

(Note the strange appearance of forward slashes in the first line and back slashes in the second. Python 3.5, Windows 7 Pro.) This shouldn't be that complicated -- what syntax is necessary to allow b to consistently import a?

----- EDIT -----

Running unittest is the question I should have found before asking this one. It also contains a great pointer to the Python Project Howto.

Community
  • 1
  • 1
Dave
  • 3,834
  • 2
  • 29
  • 44
  • 3
    Shouldn't it be `from .a import foo` since `a` is in the same directory as `b`? If you want to be able to run `b` directly from `sound/formats`, you might need to mess with `__package__` (See [PEP-0366](https://www.python.org/dev/peps/pep-0366/)). – mgilson Jan 03 '17 at 22:00
  • Nope - top works in that case, but b fails with "SystemError: Parent module ' ' not loaded, cannot perform relative import". – Dave Jan 03 '17 at 22:03
  • 1
    Right. See my comment continuation about `__package__`. – mgilson Jan 03 '17 at 22:03
  • Are you sure it's not just setting your PythonPath environment variable? See http://stackoverflow.com/questions/3701646/how-to-add-to-the-pythonpath-in-windows-7 – Scott Mermelstein Jan 03 '17 at 22:09
  • 1
    I think you're confused about what absolute imports are. It looks like you're expecting an implicit relative import. – user2357112 Jan 03 '17 at 22:11
  • @mgilson, Yes, I was confused about the fact that running a file as a script yields different results than running the same file as a module. From a script, an absolute `from a import foo` works properly because sys.path includes the folder containing a and b. It would be nice if the default import hooks (PEP 302) automatically behaved as if the package root were always present in sys.path. That way relative imports would work consistently regardless of whether a file is invoked as a script or a module. – Dave Jan 04 '17 at 18:44

1 Answers1

5

That's really not how you should be running your code.

With Python packages you shouldn't be in the folder that contains the files when you're running the code. That'd be like going to C:\Python35\Lib\site-packages\http\* and then running py -3 server.py. Ew. Don't do that.

Instead, use Python the way it expects to be used!

⚘ python --help | grep -e -m                                     
usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ...
-m mod : run library module as a script (terminates option list)

You're creating modules, but you want to be able to run them as a script. Turns out that you can do that!

> cd ../..
> dir
sound
> python -m sound.formats.b
foo
> python -m sound.top
foo

*I think that's the path but it's been a while since Windows for me. If it's wrong, I'm just trying to make a point here!

Wayne Werner
  • 49,299
  • 29
  • 200
  • 290
  • 1
    `grep -e -m` might be a little more idiomatic shell scripting, btw. – Charles Duffy Jan 03 '17 at 22:15
  • Using `grep` and a Windows path in the same post... But yap, the path is correct. – linusg Jan 03 '17 at 22:17
  • 1
    @linusg and I'm on a Mac. Go figure ;) – Wayne Werner Jan 03 '17 at 22:19
  • 1
    I have one subproject with some code and the unit tests for that code. I have a second subproject that needs to import from the first. Is there a best practice for creating unittests that aren't run from within the directory containing the code? The example I was following had `test_foo.py` containing `if __name__ = "__main__": unittest.main()`. – Dave Jan 03 '17 at 22:22
  • I prefer to keep my unittests separate. In your case I would have `project/sound/` and `project/tests/`. Then in my tests directory, in my tests files, I would have `import sound` or possibly `from sound.format import a` or something to that effect. I also create a `venv` to install my project into. – Wayne Werner Jan 03 '17 at 22:29