Target an installation as simple as the command pip install <mypackage>.

Step 0 : use virtualenv if needed

For those who don’t know it, virtualenv create a whole local python environment. This way, when using this environment, all your installed packages are installed in a folder of your choice, and you don’t risk to perturbate your OS python. You can also choose to use a dedicated python version for your project. At least two tools exists to this end :

Step 1 : prepare a proper folder architecture

Using a tool to create your project skeleton

Probably the easiest way. I know of two tools : cookiecutter, and pyscaffold. I prefer pyscaffold, because it was simpler to use at my level. But cookiecutter may be interresting if you want to fully customize the package skeletton you create.

So with pyscaffhold :

  • pip install pyscaffhold
  • putup <myproject>

It will create a folder called myproject with the following arborescence :

├── AUTHORS.rst
    ├── CHANGELOG.rst
    ├── docs
    │   ├── authors.rst
    │   ├── changelog.rst
    │   ├── conf.py
    │   ├── index.rst
    │   ├── license.rst
    │   ├── Makefile
    │   └── _static
    ├── LICENSE.txt
    ├── README.rst
    ├── requirements.txt
    ├── setup.cfg
    ├── setup.py
    ├── src
    │   └── myproject
    │       ├── __init__.py
    │       └── skeleton.py
    └── tests
        ├── conftest.py
        └── test_skeleton.py

This is quite straight forward. The only two things you need to package your program is to :

  • Add some source to src/myproject (myproject will be your installed package name)
  • Modify setup.cfg to satisfy your needs. It stores all the packaging configuration information.

Then, of course, you’ll have to add data into doc, test, and other files, if you pretend to create a state of the art package. But to create a simply “pip-instalable” package, it suffice.

Manually creating package skeleton

Read a book … I liked Serious Python from nostarch press, who is not too hard to understand and provide good advices. Manual packaging is quite tedious, since there exist many different packaging tools and formats, with pro, cons, history, some being used but not maintained, other in rapid development but not adopted yet. But you’ll have a better view of what is going on inside.

Step 2 : code

Put some python module into your src folder. Note that pyscaffold already create a simple demo app, so it is facultative.

Step 3 : generate your first package

There are a lot of different packages format. I use wheel (no argument, it was just proposed by the book I was reading). pip install wheel python setup.py bdist_wheel you’ll see that a new dist folder is created, containing a .whl (a zip archive, actually).

dist/myproject-0.0.post0.dev1+gd3252e9-py2.py3-none-any.whl One interesting point is that you can see your package have a proper version, avoiding the headache to know where and how to version your package. pyscaffold has already handled it for you, and you just have to set git tags of the form major.minor to update your package version.

Step 4 : Add your dependencies

If your package is using another one as a dependency, say pygobject for example, you’ll need to declare it. Dependencies are appended to [options] in setup.cfg, by filling install_requires key :

[options]
...
install_requires=
    your-dependency-1
    your-dependency-2
...

Step 5 : Wait, my data is not copied

If you have some data, say a config file that is not python code, or some image resources, you’ll have to make some tweaking :

  • Create a specific folder for your data files, say src/myproject/data
  • Create an empty __init__.py file at the root of this folder
  • Add your data files in this folder
  • Add the following chapter to setup.cfg:
[options.package_data]
* =
    *.conf
    *.json
    # whatever data file globbing you need
  • Use your data in your code as shown below:
import importlib.resources as pkg_resources
import json

with pkg_resources.path(data,'config.json') as path:
    json_file = open(path)
    config = json.load(json_file)

Step 6 : What if I want an executable ?

Sometimes you install a python package using pip and poof ! An executable is added to the path. This is done using entry points.

To create an entry point, you add the following to your setup.cfg :

[options.entry_points]
console_scripts =
    script_name = myproject.module:function

This will create an executable called script_name in the path, that will call function function of module module.

Step 7 : Ok, now I want a distribuable package

Many options for you :

  • Share your .wheel. It can be installed with pip install your_package.wheel
  • Use Pypi, so user can type pip install your package with pip install your package. As said before, I haven’t done that so far.
  • Debian package using stdeb : some complications for that. Read stdeb doc for more info.
  • Cross platform packaging with pyinstaller some complications may occurs as well.

Conclusion

Use pyscaffold to create a proper package architecture, and replace / add what you need from the generated files is the best option in my opinion.