How do you organise a python project that contains multiple packages so that each file in a package can still be run individually?
<p><strong>TL;DR</strong></p>

<p>Here's an example repository that is set up as described in the first diagram (below): <a href="https://github.com/Poddster/package_problems" rel="nofollow">https://github.com/Poddster/package_problems</a></p>

<p>If you could please make it look like the second diagram in terms of project organisation and can still run the following commands, then you've answered the question:</p>

<pre><code>$ git clone https://github.com/Poddster/package_problems.git
$ cd package_problems
&lt;do your magic here&gt;

$ nosetests

$ ./my_tool/my_tool.py
$ ./my_tool/t.py
$ ./my_tool/d.py

 (or for the above commands, $ cd ./my_tool/ &amp;&amp; ./my_tool.py is also acceptable)
</code></pre>

<p>Alternatively: Give me a different project structure that allows me to group together related files ('package'), run all of the files individually, import the files into other files in the same package, and import the packages/files into other package's files.</p>

<hr>

<h1>Current situation</h1>

<p>I have a bunch of python files. Most of them are useful when callable from the command line i.e. they all use argparse and <code>if __name__ == "__main__"</code> to do useful things.</p>

<p>Currently I have this directory structure, and everything is working fine:</p>

<pre><code>.
âââ config.txt
âââ docs/
âÂ Â  âââ ...
âââ my_tool.py
âââ a.py
âââ b.py
âââ c.py
âââ d.py
âââ e.py
âââ README.md
âââ tests
âÂ Â  âââ __init__.py
âÂ Â  âââ a.py
â Â  âââ b.py
âÂ Â  âââ c.py
âÂ Â  âââ d.py
âÂ Â  âââ e.py
âââ resources
    âââ ...
</code></pre>

<p>Some of the scripts <code>import</code> things from other scripts to do their work. But no script is merely a library, they are all invokable. e.g. I could invoke <code>./my_tool.py</code>, <code>./a.by</code>, <code>./b.py</code>, <code>./c.py</code> etc and they would do useful things for the user. </p>

<p>"my_tool.py" is the main script that leverages all of the other scripts.</p>

<h1>What I want to happen</h1>

<p>However I want to change the way the project is organised. The project itself represents an entire program useable by the user, and will be distributed as such, but I know that parts of it will be useful in different projects later so I want to try and encapsulate the current files into a package. In the immediate future I will also add other packages to this same project. </p>

<p>To facilitate this I've decided to re-organise the project to something like the following:</p>

<pre><code>.
âââ config.txt
âââ docs/
âÂ Â  âââ ...
âââ my_tool
âÂ Â  âââ __init__.py
âÂ Â  âââ my_tool.py
âÂ Â  âââ a.py
âÂ Â  âââ b.py
âÂ Â  âââ c.py
âÂ Â  âââ d.py
âÂ Â  âââ e.py
âÂ Â  âââ tests
âÂ Â      âââ __init__.py
âÂ Â      âââ a.py
âÂ Â    Â  âââ b.py
âÂ Â      âââ c.py
âÂ Â      âââ d.py
âÂ Â      âââ e.py
âââ package2
âÂ Â  âââ __init__.py
âÂ Â  âââ my_second_package.py
|   âââ ...
âââ README.md
âââ resources
    âââ ...
</code></pre>

<p>However, I can't figure out an project organisation that satisfies the following criteria:</p>

<ol>
<li>All of the scripts are invokable on the command line (either as <code>my_tool\a.py</code> or <code>cd my_tool &amp;&amp; a.py</code>)</li>
<li>The tests actually run :) </li>
<li>Files in package2 can do <code>import my_tool</code></li>
</ol>

<p>The main problem is with the import statements used by the packages and the tests.</p>

<p>Currently, all of the packages, including the tests, simply do <code>import &lt;module&gt;</code> and it's resolved correctly.  But when jiggering things around it doesn't work.</p>

<p>Note that supporting py2.7 is a requirement so all of the files have <code>from __future__ import absolute_import, ...</code> at the top.</p>

<h1>What I've tried, and the disastrous results</h1>

<h2>1</h2>

<p>If I move the files around as shown above, but leave all of the import statements as they currently are:</p>

<ol>
<li><code>$ ./my_tool/*.py</code> works and they all run properly</li>
<li><code>$ nosetests</code> run from the top directory doesn't work. The tests fail to import the packages scripts.</li>
<li>pycharm highlights import statements in red when editing those files :(</li>
</ol>

<h2>2</h2>

<p>If I then change the test scripts to do:</p>

<pre><code>from my_tool import x
</code></pre>

<ol>
<li><code>$ ./my_tool/*.py</code> still works and they all run properly</li>
<li><code>$ nosetests</code> run from the top directory doesn't work. Then tests can import the correct scripts, but the imports in the scripts themselves fail when the test scripts import them. </li>
<li>pycharm highlights import statements in red in the main scripts still :(</li>
</ol>

<h2>3</h2>

<p>If I keep the same structure and change <em>everything</em> to be <code>from my_tool import</code> then:</p>

<ol>
<li><code>$ ./my_tool/*.py</code> results in <code>ImportError</code>s </li>
<li><code>$ nosetests</code> runs everything ok. </li>
<li>pycharm doesn't complain about anything </li>
</ol>

<p>e.g. of 1.:</p>

<pre><code>Traceback (most recent call last):
  File "./my_tool/a.py", line 34, in &lt;module&gt;
    from my_tool import b
ImportError: cannot import name b
</code></pre>

<h2>4</h2>

<p>I also tried <code>from . import x</code> but that just ends up with <code>ValueError: Attempted relative import in non-package</code> for the direct running of scripts.</p>

<h1>Looking at some other SO answers:</h1>

<p>I can't just use <code>python -m pkg.tests.core_test</code> as</p>

<p>a) I don't have <strong>main</strong>.py. I guess I could have one?<br>
b) I want to be able to run all of the scripts, not just main?</p>

<p>I've tried:</p>

<pre><code>if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
</code></pre>

<p>but it didn't help. </p>

<p>I also tried:</p>

<pre><code>__package__ = "my_tool"
from . import b
</code></pre>

<p>But received:</p>

<pre><code>SystemError: Parent module 'loading_tool' not loaded, cannot perform relative import
</code></pre>

<p>adding <code>import my_tool</code> before <code>from . import b</code> just ends up back with <code>ImportError: cannot import name b</code></p>

<h2>Fix?</h2>

<p>What's the correct set of magical incantations and directory layout to make all of this work?</p>

<p>Once you move to your desired configuration, the absolute imports you are using to load the modules that are specific to <code>my_tool</code> no longer work.</p>

<p>You need three modifications after you create the <code>my_tool</code> subdirectory and move the files into it:</p>

<ol>
<li><p>Create <code>my_tool/__init__.py</code>. (You seem to already do this but I wanted to mention it for completeness.)</p></li>
<li><p>In the files directly under in <code>my_tool</code>: change the <code>import</code> statements to load the modules from the current package. So in <code>my_tool.py</code> change:</p>

<pre><code>import c
import d
import k
import s
</code></pre>

<p>to: </p>

<pre><code>from . import c
from . import d
from . import k
from . import s
</code></pre>

<p>You need to make a similar change to all your other files. (You mention having tried setting <code>__package__</code> and then doing a relative import but setting <code>__package__</code> is not needed.)</p></li>
<li><p>In the files located in <code>my_tool/tests</code>: change the <code>import</code> statements that import the code you want to test to relative imports that load from one package up in the hierarchy. So in <code>test_my_tool.py</code> change:</p>

<pre><code>import my_tool
</code></pre>

<p>to:</p>

<pre><code>from .. import my_tool
</code></pre>

<p>Similarly for all the other test files.</p></li>
</ol>

<p>With the modifications above, I can run modules directly:</p>

<pre><code>$ python -m my_tool.my_tool
C!
D!
F!
V!
K!
T!
S!
my_tool!
my_tool main!
|main tool!||detected||tar edit!||installed||keys||LOL||ssl connect||parse ASN.1||config|

$ python -m my_tool.k
F!
V!
K!
K main!
|keys||LOL||ssl connect||parse ASN.1|
</code></pre>

<p>and I can run tests:</p>

<pre><code>$ nosetests 
........
----------------------------------------------------------------------
Ran 8 tests in 0.006s

OK
</code></pre>

<p>Note that I can run the above both with Python 2.7 and Python 3.</p>

<hr>

<p>Rather than make the various modules under <code>my_tool</code> be directly executable, I suggest using a proper <code>setup.py</code> file to declare entry points and let <code>setup.py</code> create these entry points when the package is installed. Since you intend to distribute this code, you should use a <code>setup.py</code> to formally package it anyway.</p>

<ol>
<li><p>Modify the modules that can be invoked from the command line so that, taking <code>my_tool/my_tool.py</code> as example, instead of this:</p>

<pre><code>if __name__ == "__main__":
    print("my_tool main!")
    print(do_something())
</code></pre>

<p>You have:</p>

<pre><code>def main():
    print("my_tool main!")
    print(do_something())

if __name__ == "__main__":
    main()
</code></pre></li>
<li><p>Create a <code>setup.py</code> file that contains the proper <code>entry_points</code>. For instance:</p>

<pre><code>from setuptools import setup, find_packages

setup(
    name="my_tool",
    version="0.1.0",
    packages=find_packages(),
    entry_points={
        'console_scripts': [
            'my_tool = my_tool.my_tool:main'
        ],
    },
    author="",
    author_email="",
    description="Does stuff.",
    license="MIT",
    keywords=[],
    url="",
    classifiers=[
    ],
)
</code></pre>

<p>The file above instructs <code>setup.py</code> to create a script named <code>my_tool</code> that will invoke the <code>main</code> method in the module <code>my_tool.my_tool</code>. On my system, once the package is installed, there is a script located at <code>/usr/local/bin/my_tool</code> that invokes the <code>main</code> method in <code>my_tool.my_tool</code>. It produces the same output as running <code>python -m my_tool.my_tool</code>, which I've shown above.</p></li>
</ol>

python python-2.7 import pycharm packages