Have a Python project? Compile it into a package with “pip”!
Developing a program in Python?
Chances are that if the project is even a little complex, that multiple files are involved.
Luckily, the pip
utility allows for creation of packaged up, platform agnostic programs!
Creating the package structure
First, be sure that all the required pip
packages "setuptools
" and "wheel
" are installed:
$ sudo python -m pip install --upgrade pip setuptools wheel
setuptools
- allows for binary Python packages to be builtwheel
- optional package that creates a ".whl
" binary, which is like a tarball (.tar.bz
) but builds more efficient
Then, create the package directory structure; this is the generic file structure:
$ tree
.
└── package_name
├── code_base
│ ├── _init_.py
│ └── source_code.py
├── LICENSE.md
├── README.md
├── runtime_script
└── setup.py
package_name
- the name of the created packagecode_base
- this is where the actual Python code goes_init_.py
- every package must have this; can be empty but this code is the first program code runsource_code.py
- this is the program codeLICENSE.md
- every program needs a license; this is stored hereREADME.md
- a detailed description of the packaged programruntime_script
- a shell script that executes at package runtime; explained belowsetup.py
- the actual package build instructions; explained below
runtime_script
This is a required executable file that needs to be created; during runtime of the application, any commands in this file will be run.
Below is an example of a runtime_script
file:
#!/usr/bin/env pythonecho "This will be displayed during package runtime!"
setup.py
This is the other required executable file that will need creation; these are the actual build instructions that will be executed with the pip
application
Below is an example of a setup.py
file:
from setuptools import setupsetup(
name='',
version='',
author='',
author_email='',
packages='[]',
scripts='[]',
url='',
license='',
description='',
long_description=open('').read(),
)
The first line of code:
from setuptools import setup
Exists to tell pip
to use the setuptools
module to run the package build process.
Within the setup()
object, houses all the configuration data:
setup(
name='package_name',
version='1.0.0',
author='sample',
author_email='sample@example.com',
packages=['code_base'],
scripts=['runtime_script'],
url='http://example.com',
license='LICENSE.md',
description='Enter Description here',
long_description=open('README.md').read(),
)
To further explain each field:
(*** indicates project specific filenames)
- ***
name
- the name of the created package; in this case "package_name
" version
- the version of the created programauthor
- the author of the created programauthor_email
- the author's email- ***
packages
- this is where the actual Python code resides, can take multiple arguments; in this case "code_base
" - ***
scripts
- a shell script that executes at package runtime, can take multiple arguments; in this case "runtime_script
" url
- this is where the project is available for download- ***
license
- this points to the license file; in this case "LICENSE.MD
" description
- a short description of the program- ***
long_description
- this has the functionopen('').read()
, which opens up the created 'readme' file; in this case "README.md
"
Building the binary
Now that the required scripts, directory structure and configuration is in place, the binary can now be built.
There are two options available:
- Building a tarball package — binary would have a “
*.tar.gz
" file extension; standard packaging - Building a Python “Wheel” package — binary would have a “
*.whl
" file extension; improved Python packaging
Creating a “tarball” binary package
First, navigate to the directory containing the “setup.py
" file:
$ tree
.
└── package_name <-- NAVIGATE HERE
├── code_base
│ ├── _init_.py
│ └── source_code.py
├── LICENSE.md
├── README.md
├── runtime_script
└── setup.py <-- RUN THIS
To build a tarball, run the following:
$ python setup.py sdist
The new project structure is listed below:
$ tree
.
└── package_name
├── code_base
│ ├── _init_.py
│ └── source_code.py
├── dist <-- DIRECTORY CREATED
│ └── package_name-1.0.0.tar.gz <-- TARBALL CREATED
├── LICENSE.md
├── package_name.egg-info <-- DIRECTORY CREATED
│ ├── dependency_links.txt
│ ├── PKG-INFO
│ ├── SOURCES.txt
│ └── top_level.txt
├── README.md
├── runtime_script
└── setup.py
This creates two new directories:
dist
- any tarball/wheel binaries are stored herepackage_name-1.0.0.tar.gz
is the tarball binary createdpackage_name.egg-info
- egg packages usually contains package information, dependency links, and compiled byte code; any information used bysetup.py
during tests is also captured here
Creating a “wheel” binary package
First, navigate to the directory containing the “setup.py
" file:
$ tree
.
└── package_name <-- NAVIGATE HERE
├── code_base
│ ├── _init_.py
│ └── source_code.py
├── LICENSE.md
├── README.md
├── runtime_script
└── setup.py <-- RUN THIS
To build a “wheel” binary, run the following:
$ python setup.py bdist_wheel
The new project structure is listed below:
$ tree
.
└── package_name
├── build <-- DIRECTORY CREATED
│ ├── bdist.linux-x86_64
│ ├── lib
│ │ └── code_base
│ │ ├── _init_.py
│ │ └── source_code.py
│ └── scripts-3.10
│ └── runtime_script
├── code_base
│ ├── _init_.py
│ └── source_code.py
├── dist <-- DIRECTORY CREATED
│ └── package_name-1.0.0-py3-none-any.whl <-- BINARY CREATED
├── LICENSE.md
├── package_name.egg-info <-- DIRECTORY CREATED
│ ├── dependency_links.txt
│ ├── PKG-INFO
│ ├── SOURCES.txt
│ └── top_level.txt
├── README.md
├── runtime_script
└── setup.py
This creates three new directories:
build
- platform-specific build files are stored heredist
- any tarball/wheel binaries are stored herepackage_name-1.0.0-py3-none-any.whl
is the "wheel" binary createdpackage_name.egg-info
- egg packages usually contains package information, dependency links, and compiled byte code; any information used bysetup.py
during tests is also captured here