0

So I have a project called "Pants" that lives on GitHub.com. Originally the project was a single .py file called pants.py.

Pants/
    pants.py
    README.md

and users could import and use it as follows:

import pants
w = pants.World()

Both of these feel fine to me. Then I read up on how to structure a project with unit tests included, and initially reorganized my project thusly:

Pants/
    pants.py
    README.md
    test/
        __init__.py
        test_world.py
        test_ant.py

The problem with this is that although users can still import the same logical way, there is no pants.test module/package! No problem, I thought, I'll simply add another __init__.py file:

Pants/
    __init__.py
    pants.py
    README.md
    test/
        __init__.py
        test_world.py
        test_ant.py

But now the imports feel incredibly repetitive:

import Pants.pants
w = Pants.pants.World()

It just feels like there is a better way! Right now, my project is structured like this:

Pants/
    README.md
    pants/
        __init__.py
        ants.py
        world.py
        solver.py
        test/
            __init__.py
            test_world.py
            test_ant.py

However, the import lines users are faced with are equally repetitive:

import pants.world
import pants.solver
w = pants.world.World()
s = pants.solver.Solver()

Now I know you can alias these things to shorter equivalents, such as import pants.world.World as World but the import line itself still feels repetitive. Any suggestions on how to avoid such repetition while retaining proper project structure? Does any of this have to change if I were to, say, put it up for installation via pip?

rhgrant10
  • 179
  • 8
  • take a look at [`asyncio/__init__.py`](http://hg.python.org/cpython/file/ae405c11193b/Lib/asyncio/__init__.py). If `world.py` and `solver.py` are implementation details; your users may use the flat namespace i.e., the only import is `import pants` – jfs May 16 '14 at 20:40
  • Awesome, thanks! I thought I had read somewhere that putting code into `__init__.py` was a bad practice, but after looking at how asyncio does it, along with [this SO question](http://stackoverflow.com/questions/5831148/why-would-i-put-python-code-in-init-py-files), I know exactly how to proceed :) – rhgrant10 May 18 '14 at 19:40

2 Answers2

1

To fix it, I kept my package structure the same, and added the following lines to pants/__init__.py:

from .ant import Ant
from .world import World
from .solver import Solver

Then I was able to change the import lines at the top of my demo file to:

from pants import World
from pants import Solver
tshepang
  • 12,111
  • 21
  • 91
  • 136
rhgrant10
  • 179
  • 8
  • Since I don't have the reputation for upvoting an answer to my question, and also since the help I received was technically found in links, I thought I would answer my own question explicitly. That way the next person to have the question can directly see how the changes I made to solve my problem without relying on the existence of link content. – rhgrant10 May 18 '14 at 19:51
0

Instead of having a separate pants.py put that code inside of __init__.py. Then when someone does import pants it loads that __init__.py. (I'd avoid using the uppercase "Pants" as uppercase is generally for classnames)

If your users need world or solver separately you can also do

from pants import world,solver
w = world.World()
s = solver.Solver()

If they wanted everything from your package they could do

from pants import *
Cfreak
  • 19,191
  • 6
  • 49
  • 60