This document discusses the build process and the basic layout of the project.
The build process is setup so that you can develop in one of two ways
- using only python, or
- using gradle (which calls the python setup anyway).
We use the gradle setup in conjunction with our CI process and Jenkins since it encapsulates the build tool chain.
In either case before you start development you will need to do the following.
- Clean the folder - this is useful if you are reseting the workspace.
- You will need a virtual environment for the development process.
- Python-Only: You will need to manually create one. We typically keep
it under the project directory
You will need to remember to activate the environment before you run any of the subsequents steps.
export venvdir=~/venvs/buildproject virtualenv -p python3 ${venvdir} source ${venvdir}/bin/activate
- Gradle: When you run the any of the gradle build, test or lint tasks, gradle
will automatically ensure you have a virtual environment. The default location is
<repo_dir>/venv/x3
. It also creates the symlink<repo_dir>/activate -> <repo_dir>/venv/x3/bin/activate
to simplify access.
- Python-Only: You will need to manually create one. We typically keep
it under the project directory
- Install the dependencies into the environment (see the "Build Tasks" table below).
Typically you will want to use (A) the standard dev setup, or (B) the interactive setup.
The second one if you setup your project with tooling specified in the
interactive_dev_deps
group insetup.py
. Use it to install modules like Jupyter. Note that for continuous integration, and manual version management you will need (C).
These are a list of common tasks necessary for building and testing. Note that the
gradle commands can be abbreviated by initials - i.e. ./gradlew <abbrev>
using
the abbreviations shown below.
Task info | Gradle | Python |
---|---|---|
Clean workspace This deletes all generated files and folders |
./gradlew cleanAll Abbrev: cA |
rm -rf dist/ build/ .eggs/ \ .pytest_cache ${project.name}.egg-info |
Setup standard dev (A) | ./gradlew installDependencies Abbrev: iD |
pip install -e .[test_group] -e .[lint_group] |
Setup interactive dev (B) support e.g. Jupyter |
./gradlew installInteractiveDevDependencies Abbrev: iIDD |
pip install -e .[test_group] -e .[lint_group]\ -e .[interactive_dev_group] |
Setup CI environment (C) Auto installs (A) |
./gradlew installCIDependencies Short: ./gradlew iCID |
- |
Run linter | ./gradlew lint |
flake8 --count --exit-zero {{ cookiecutter.python_module_name }} |
Run tests | ./gradlew testPackage Abbrev: tP , test |
python setup.py test |
Build wheel | ./gradlew build This always runs lint and test. |
python setup.py bdist_wheel |
List all gradle tasks | ./gradlew tasks |
|
To run your console scripts on the command line you will want the virtual environment activated. Additionally for development, you will need to ensure your module is source-installed in editable mode into the environment.
- Python: Activate the virtual env and run
pip install -e .
in the project directory. - Gradle: If you run the
test
orbuild
task once, will automatically be source-installed into the default environment. Note that you will also have theactivate
symlink in the project directory to activate the environment.
There are scenarios where you want to work on multiple python modules simultaneously (each from a separate repo), e.g., one module depends on another and you editing both. In that scenario you will have to pick one virtual environment and do source install into it from all the relevant module repos.
Assuming we have project A and B with modules mod_a
and mod_b
, where mod_b
depends on mod_a
.
We want to work on both together. Let's use the virtual environment created by gradle for B. To setup the enviroment
for development on both A and B, I do the following.
# We are in B's workspace. First setup the virtual environment
$B> ./gradlew cleanAll installDependencies
# Frist activate the environment. Not that gradle creates a convenient symlink to activate the environment
$B> . activate
# If you ran ./gradlew build or ./gradlew test
# it will already source install the module for B. For the example we do it again anway.
(x3)$B> pip install -e .
# Now cd to the director for A, and install it as source
(x3)$B> cd ../A
(x3)$A> pip install -e .
Now code edits in either A or B will be available in the "installed" setup. Note that if you add or remove console
script files (the ones mentioned in entry_points
in setup.py
), you will need to run the source code install
step again.
The version management happens in the continuous integration process. This relies on using semantic versioning combined with specifications in PEP-440.
The development version is of the form <major>.<minor>.<patch>.dev<devbuild>
, where each component is an integer. When changes are merged into the master branch, the CI process will increment the <devbuild>
: X.Y.Z.devN
-> X.Y.Z.devN+1
.
For a release, the CI process will drop the last component, creating version <major>.<minor>.<patch>
: X.Y.Z.devN
-> X.Y.Z
.
Upon publishing and tagging after a release, it will increment the <patch>
number: X.Y.Z
-> X.Y.Z+1.dev0
.
If your next release requires an increase in the <major>
or <minor>
versions, You can use the gradle tasks to increment the
version using the bumpVersionPostRelease
task with an additional parameter
# To increment the major part
./gradlew bumpVersionPostRelease -PbumpPartOverride=major
# To increment the minor part
./gradlew bumpVersionPostRelease -PbumpPartOverride=minor
Note that the gradlew tasks calls the Python bumpversion
module as part of the task, but it also provides a number of checks to avoid complications from certain bumpversion
behavior.