Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #60394 > unrolled thread

Understanding relative imports in package - and running pytest with relative imports?

Started byVictor Hooi <victorhooi@gmail.com>
First post2013-11-24 17:30 -0800
Last post2013-11-24 22:00 -0800
Articles 2 — 2 participants

Back to article view | Back to comp.lang.python


Contents

  Understanding relative imports in package - and running pytest with relative imports? Victor Hooi <victorhooi@gmail.com> - 2013-11-24 17:30 -0800
    Re: Understanding relative imports in package - and running pytest with relative imports? Devin Jeanpierre <jeanpierreda@gmail.com> - 2013-11-24 22:00 -0800

#60394 — Understanding relative imports in package - and running pytest with relative imports?

FromVictor Hooi <victorhooi@gmail.com>
Date2013-11-24 17:30 -0800
SubjectUnderstanding relative imports in package - and running pytest with relative imports?
Message-ID<08fbd202-ebdf-4a41-a493-bc3b76a6c227@googlegroups.com>
Hi,

Ok, this is a topic that I've never really understood properly, so I'd like to find out what's the "proper" way of doing things.

Say I have a directory structure like this:

    furniture/
        __init__.py
        chair/
            __init__.py
            config.yaml
            build_chair.py
        common/
            __init__.py
            shared.py
        table/
            __init__.py
            config.yaml
            create_table.sql
            build_table.py

The package is called furniture, and we have modules chair, common and table underneath that.

build_chair.py and build_table.py are supposed to import from common/shared.py using relative imports. e.g.:

    from ..common.shared import supplies

However, if you then try to run the scripts build_chair.py, or build_table.py, they'll complain about:

    ValueError: Attempted relative import in non-package

After some Googling:

http://stackoverflow.com/questions/11536764/attempted-relative-import-in-non-package-even-with-init-py
http://stackoverflow.com/questions/72852/how-to-do-relative-imports-in-python
http://stackoverflow.com/questions/18888198/getting-attempted-relative-import-in-non-package-error-in-spite-of-having-init
http://stackoverflow.com/questions/14664313/attempted-relative-import-in-non-package-although-packaes-with-init-py-in
http://melitamihaljevic.blogspot.com.au/2013/04/python-relative-imports-hard-way.html

The advice seems to be either to run it from the parent directory of furniture with:

    python -m furniture.chair.build_chair

Or to have a main.py outside of the package directory and run that, and have it import things.

However, I don't see having a separate single main.py outside my package would work with keeping my code tidy/organised, and or how it'd work with the other files (config.yaml, or create_table.sql) which are associated with each script?

A third way I thought of way just to create a setup.py and install the package into site-packages - and then everything will work? However, I don't think that solves my problem of understanding how things work, or getting my directory structure right.

Although apparently running a script inside a package is an anti-pattern? (https://mail.python.org/pipermail/python-3000/2007-April/006793.html)

How would you guys organise the code above?

Also, if I have tests (say with pyttest), inside furniture/table/tests/test_table.py, how would I run these as well? If I run py.test from there, I get the same:

    $ py.test
     ....
     from ..table.build_table import Table
     E   ValueError: Attempted relative import in non-package
    ....

(Above is just an extract).

Assuming I use pytest, where should my tests be in the directory structure, and how should I be running them?

Cheers,
Victor

[toc] | [next] | [standalone]


#60399

FromDevin Jeanpierre <jeanpierreda@gmail.com>
Date2013-11-24 22:00 -0800
Message-ID<mailman.3159.1385359300.18130.python-list@python.org>
In reply to#60394
On Sun, Nov 24, 2013 at 5:30 PM, Victor Hooi <victorhooi@gmail.com> wrote:
> The advice seems to be either to run it from the parent directory of furniture with:
>
>     python -m furniture.chair.build_chair

Yes. More pedantically, run it from somewhere such that the furniture
package is importable. For example, if you install furniture, then
it's accessible everywhere.

> However, I don't see having a separate single main.py outside my package would work with keeping my code tidy/organised, and or how it'd work with the other files (config.yaml, or create_table.sql) which are associated with each script?

The other files are contained within the package. As long as
build_table looks in the right places, they'll still be accessible.
(e.g. http://docs.python.org/2/library/pkgutil#pkgutil.get_data ).

> A third way I thought of way just to create a setup.py and install the package into site-packages - and then everything will work? However, I don't think that solves my problem of understanding how things work, or getting my directory structure right.

No, installing furniture won't change anything about how you need to
run it. Either you run a separate script that imports it and runs the
right main function, or you use python -m furniture.foo.bar

> Although apparently running a script inside a package is an anti-pattern? (https://mail.python.org/pipermail/python-3000/2007-April/006793.html)

It's not an antipattern, it's just plain broken. Some things stop
working as you'd expect, like exception handling.

> How would you guys organise the code above?

If I really cared about the command line interface, I would probably
reorganize something like this:

furniture/
 - __init__.py
 - __main__.py
 - common.py
 * chair/
   - __init__.py
   - build.py
   - create.sql
   - config.yaml
 * furniture/
   - __init__.py
   - build.py
   - create.sql
   - config.yaml

And then create chairs by running `python -m furniture build chair`.
furniture/__main__.py would dynamically import furniture.chair.build
and run furniture.chair.build.main().

There would also be an executable I might install separately,
furniture, the contents of which are "from furniture import __main__;
__main__.main()"

> Also, if I have tests (say with pyttest), inside furniture/table/tests/test_table.py, how would I run these as well? If I run py.test from there, I get the same:
>
>     $ py.test
>      ....
>      from ..table.build_table import Table
>      E   ValueError: Attempted relative import in non-package
>     ....
>
> (Above is just an extract).
>
> Assuming I use pytest, where should my tests be in the directory structure, and how should I be running them?

As far as I understand py.test and nose, they are designed such that
you don't put your tests as subpackages. If you want to put your tests
as subpackages, use a different unit testing framework, like unittest.
Then you can just do `python -m unittest discover .`

If you do want to use py.test (or nose), then either there is some
flag you need to pass in that I'm not aware of, or your directory
structure should be:

my-project/
 - README
 - LICENSE
 - setup.py
 * furniture/
    ...
 * tests/
   - test_chair.py
   - test_common.py
   - test_table.py

Note that tests is not a package either. py.test and nose work by
loading files, NOT modules. So they won't work with loading test
submodules in a package.

For this and other reasons, I personally stay away from nose and py.test.

-- Devin

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web