Let’s make this code a package by adding an __init__.py and putting the __init__.py file and module in a directory with a package name:
| $ tree some_package_proj/ |
| some_package_proj/ |
| ├── setup.py |
| └── src |
| └── some_package |
| ├── __init__.py |
| └── some_module.py |
The content of some_module.py doesn’t change. The __init__.py needs to be written to expose the module functionality to the outside world through the package namespace. There are lots of choices for this. I recommend skimming the two sections of the Python documentation[54] that cover this topic.
If we do something like this in __init__.py:
| import some_package.some_module |
the client code will have to specify some_module:
| import some_package |
| some_package.some_module.some_func() |
However, I’m thinking that some_module.py is really our API for the package, and we want everything in it to be exposed to the package level. Therefore, we’ll use this form:
| from some_package.some_module import * |
Now the client code can do this instead:
| import some_package |
| some_package.some_func() |
We also have to change the setup.py file, but not much:
| from setuptools import setup, find_packages |
| |
| setup( |
| name='some_package', |
| packages=find_packages(where='src'), |
| package_dir={'': 'src'}, |
| ) |
Instead of using py_modules, we specify packages.
This is now installable:
| $ cd /path/to/code/appendices/packaging |
| $ pip install ./some_package_proj/ |
| Processing ./some_package_proj |
| Installing collected packages: some-package |
| Running setup.py install for some-package ... done |
| Successfully installed some-package-0.0.0 |
and usable:
| $ python |
| Python 3.6.1 (v3.6.1:69c0db5050, Mar 21 2017, 01:21:04) |
| [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin |
| Type "help", "copyright", "credits" or "license" for more information. |
| >>> from some_package import some_func |
| >>> some_func() |
| 42 |
Our project is now installable and in a structure that’s easy to build on. You can add a tests directory at the same level of src to add our tests if you want. However, the setup.py file is still missing some metadata needed to create a proper source distribution or wheel. It’s just a little bit more work to make that possible.