Creating an Installable Package

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:

appendices/packaging/some_package_proj/src/some_package/__init__.py
 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:

appendices/packaging/some_package_proj/setup.py
 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.