Overview
Projector helps you set up isolated environments for developing Python projects, and pack these environment into isolated packages that you can later install on production.
At Infinidat, we build software for system administrators, that expect a complete, packaged product, and not just a bunch of Python scripts. Since Python (at least until Python 3.2) does not provide a decent mechanism for building Python applications (with a bundled interpreter), we started building our own solution, and this is what we came up with.
By using some skeleton, buildout
recipes and some wrapping code, we provide:
- Isolated python builds for your applications
- Simple, easy-to-use version release mechanism
- Packaging Python applications into MSI, RPM, DEB
Using Projector
Installation
Projector is available on pypi.python.org
, as infi.projector
. To install just run:
easy_install -U infi.projector
Walkthrough
We will explain the bits and bolts about using project by a straight-forward walkthrough.
Creating a new project
We will start by creating a new project:
projector repository init --mkdir infi.example git@github.com:Infinidat/example.git "example on infi.projector" "An example by walkthrough"
This creates a new skeleton for the project, under a new directory, called infi.example`. Inside, you'll find the following files:
bootstrap.py
. This file is buildout's boot strapper, used to start building the development environmentbuildout.cfg
. This project's configuration file. Already includes the project name and description strings.buildout.in
. Yet another configuration file, used in production environments.setup.in
. A template file for setup.py. Modify when necessary.
Also, you'll notice that the checked out branch is develop
. That is because we use gitflow
's branching model. That means that master
holds merges for final releases only, and that the development is on develop
or separate feature branches.
Building the development environment
If you just create a new project, or clone an existing one, you won't have a proper setup.py, or any console script. In order to generate those, run:
projector devenv build
This script will create the buildout environment, generate the version files from Git, and create the executable scripts under the bin
directory.
If you want to use our isolated python builds in our development environment, and not the global Python, run instead:
projector devenv build --isolated-python
This will download the platform-specific build from our servers, and use that.
There are other flags for this command, you can read about them by passing --help
.
Some environment variables may change the behavior of the build process. Specifically, PROJECTOR_BOOTSTRAP_DOWNLOAD_BASE
and PROJECTOR_BOOTSTRAP_SETUP_SOURCE
change
the command-line parameters passed to bootstrap.py
(--download-base
and --setup-source
, respectively).
Adding dependencies
Projects handled by projector
have two types of dependencies:
production
dependencies. written to setup.py, honored bypip
/easy_install
development
dependencies. written to buildout.cfg, affects only the development environment.
Handling both types of dependencies/requirements is easy:
projector requirements list [--development]
projector requirements add <requirement> [--development] [--commit-changes]
projector requirements remove <requirement> [--development] [--commit-changes]
Freezing versions
You can freeze
the development environment by using the following commands:
projector requirements freeze [--with-install-requires] [--commit-changes] [--newest]
projector requirements unfreeze [--with-install-requires] [--commit-changes]
What this does is adds the versions of the downloaded dependencies to buildout.cfg
, so that you'll always get the same set of dependencies when you run devenv build
.
If you pass the --with-install-requires
flag, this will also update the dependencies in install_requires
with a >=
requirement to the locally installed version.
Console scripts
projector
provide a simple command-line interface to manage console_scripts
entry points in setup.py:
projector console-scripts list
projector console-scripts add <script-name> <entry-point> [--commit-changes]
projector console-scripts remove <script-name> [--commit-changes]
Adding package data
If you wish to include additional (non-Python-source) files in your python package distribution, projector
can help you set it up:
projector package-data list
projector package-data add <filename> [--commit-changes]
projector package-data remove <filename> [--commit-changes]
Releasing versions
As we mentioned earlier, we use gitflow's branching model and versioning scheme. However, there's a little more to do when releasing versions than just running one git-flow
command:
- You need to find out the latest version number, and advance on top of it
- You need to bump the version-related files in the source
- You need to upload the version to PyPI
- You need to push the commit and tags to origin
projector
solves all of these problems with the following command-line options:
projector version release <version> [--no-fetch] (--no-upload | [--distributions=DISTRIBUTIONS] [--pypi-servers=PYPI_SERVERS]) [--push-changes]
projector version upload <version> [--distributions=DISTRIBUTIONS] [--pypi-servers=PYPI_SERVERS]
Where the options are:
release
. Release a new version, including registering and uploading to pypiupload
. Upload an existing version to pypi--distributions=DISTRIBUTIONS
. Distributions to build [default: sdist,bdist_egg]--pypi-servers=PYPI
. PyPI server for publishing [default: pypi,]<version>
. x.y.z, or the keywords: major, minor, trivial (for 'release' only) or current, latest (for 'upload' command only)--no-upload
. Do not upload the package as part of the release process--no-fetch
. Do not fetch origin before releasing--push-changes
. We often forget to push the commits and tags to origin. This option does it for you
Packaging applications (not just modules)
If you're developing a complete application, and not just a python package, you probably want to generate a sysadmin-friendly package of your app, bundled with its own iterpreter.
In order to do this, you will have to add the following dependency to your project by running the command:
projector requirements add infi.recipe.console_scripts
Then, you'll need to build the development environment with the isolated python included:
projector devenv build --isolated-python
After the environment is ready, you can use projector
to build stand-alone, isolated, packages. just run:
projector devenv pack
Based on the platform you're running on, this command will generate a package under the parts
directory:
- MSI on Windows platform
- DEB on ubuntu
- RPM on redhat/centos
The target arhicture of the package will also match the platform you are using (x86 for 32bit, x64 for 64bit)
Project/User defaults
In some cases, you'd want to always use a set of command-line arguments for a specific project, or maybe some user will want to use the same arguments for all their projects.
You can do so by writing the following configuration file to ./.projector
(per-project) or ~/.projector
(per-user):
[commandline-arguments]
--use-isolated-python = True
--pypi-servers=pypi,local
This is just an example of course.
Developing projector
Checking out the code
The easiest way to checkout the code is by using projector itself:
easy_install infi.projector
projector clone git://github.com/Infindiat/infi.projector.git
cd infi.projector
projector devenv build
There are two alternatives.
The first one using the environment python and requires you to install dependencies (it'll tell you what they are):
python src/infi/projector/first_run/with_environment_python.py
The second does not modify the environment python, it uses only the buildout environment, and, it can be used with our isolated python build:
python src/infi/projector/first_run/without_environment_python.py [--use-isolated-python]