0

I'm upgrading a bunch of scripts where the ecosystem is a bit of a mess. The scripts always relied on external modules, and didn't have any package infrastructure of their own (they also didn't do much OOP, as you can imagine). There's nothing at the top level, but it is the working directory when starting Python and I'd like to keep it that way. At the top-level, I've just created __init__.py file (based on another question). As I'm less experienced with Python __init__.py confuses me a bit. All of the __init__.py files I've created are empty, it's my understanding that this is all that's required.

Assume I have the following directory structure:

__init__.py
dev.properties
prod.properties
F/
  Foo.py
  __init__.py

B/
  bar.py
  __init__.py

And the code is like this :

# Foo.py
    from ..b import bar
    barFunc()
# bar.py
    def barFunc():
        print "Hello, World!"
        sys.stdout.flush()

I've created __init__.py at the root, in F/ and in B/. However, when I run python F/Foo.py, I get an error:

Traceback (most recent call last):
  File "F/Foo.py", line 3, in <module>
    from ..b import bar
ValueError: Attempted relative import in non-package

What exactly would I need to do to invoke python F/Foo.py and be able to depend on things defined in sibling directories?

Update

Thanks to @user2455127, I realized that I forgot to remove the file extension .py and my working directory was wrong. From the mypackage directory, running python -m mypackage/F/Foo, the error was : myvirtualenv/bin/python: No module named mypackage/B/bar.

Re-reading @user2455127's post, I ran from the directory above and get a long Traceback:

Traceback (most recent call last):
  File "/usr/lib/python2.7/runpy.py", line 162, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "<full path>/mypackage/foo/Foo.py", line 24, in <module>
    from ..b import bar
ValueError: Attempted relative import in non-package

I'm not quite sure what needs to be done to fix this, but it seems like the __package__ attribute may help. I'll try and figure that out, and post another update.

Community
  • 1
  • 1
blong
  • 2,815
  • 8
  • 44
  • 110

2 Answers2

1

Have a look to this : Attempted relative import in non-package even with init.py and brenBarn's answer

If you run it from the folder upper than the one with dev.properties and the others files (called lambda in my case), with this command line :

python -m lambda.F.Foo

it works.

Community
  • 1
  • 1
  • So, are you saying that in your case, the the directory above `F` is called `lambda`? Mimicking that, I've run `python mypackage/F/Foo.py`, but I get an error: `myvirtualenv/bin/python: Import by filename is not supported.` – blong Dec 02 '15 at 16:56
  • yes, but read the link, and your command is wrong, look at mine, there is the "-m" option and no ".py" in the end, to call it as a package. Plus I call it from the folder containing "lambda" – user2455127 Dec 02 '15 at 16:57
  • Ah, sorry, I actually did have the `-m` (command was right, my SO comment was wrong), but didn't realize you removed the `.py` extension. Re: calling folder, I guess I've got that part wrong (working dir was `mypackage`). – blong Dec 02 '15 at 17:00
  • Well, again I recommend you to read the link I gave you ;) I am not sure if what you do is really what you will have in your project, is Foo.py run as the main or imported ? Because relative imports depends on which script you run. I can't answer you here since there's plenty of options, that you will find on the related question that I linked ! – user2455127 Dec 02 '15 at 17:09
  • I appreciate it, and I'll keep trying to figure it out. I did post an update to my question if you're interested. – blong Dec 02 '15 at 17:20
1

If the current working directory is F's and B's parent directory, then F and B are available as modules to all Python code. You should run:

$ F/foo.py

and then F/foo.py should contain

from B.bar import barFunc
barFunc()

As for __init__.py, that file's existance simply makes the directory an importable module. If you want to know more about it, check the docs on how imports work. (For most people, reading all of that isn't necessary.)

Relative imports can be pretty messy, so I'd advise shying away from them for now.

Mark Ignacio
  • 421
  • 3
  • 8
  • Aha! That solved it for me! :) It was the line `from ..b import bar` that tripped me up. Now using `from B.bar import barFunc`, I can run both `python -m F.Foo` and `python -m F/Foo` from the parent directory of `F/` and `B/`. *However*, it's unclear to why going up one directory and running `python -m mypackage.F.Foo` (or `python -m mypackage/F/Foo`) result in `ImportError: No module named b.bar`. I'm happy to go on not knowing, but I am curious :) – blong Dec 02 '15 at 17:46
  • It's all about the [PYTHONPATH](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH) - Python looks in those directories for importable things, and the current working directory is one of them. – Mark Ignacio Dec 02 '15 at 17:54