Setting up Intellisense for Zato ESB Development

Posted 2018-08-16 by kbni [permalink] [tagged bash zato python]

A big part of my current work is developing integrations, I use Zato as a platform for this. I don't want to go too much into what Zato is, but It's open source, Python and it's awesome.

One of my pet peeves about Zato is that it's not as friendly as it could be to develop for, in a few ways:

  • It's difficult to write testing for Zato services
  • It's Python 2 (still, in 2018)
  • When running Zato inside of containers without sshd, it's difficult to remotely scan a working interpreter for intellisense

(Even with all of the above, Zato is pretty great!)

For this reason, I have the following script which sets up a local pyenv-virtualenv Python environment with Zato's sources as well as my zato_extra_paths.
#!/usr/bin/env bash
#  Script: (Create virtualenv with Zato)
#  Author: Alex Wilson <>
#  This script will setup python's site-packages with the zato modules and your zato_extra_sources
#  modules. It's not expected that you will actually use these as modules, it is purely to setup
#  intellisense for your IDE much easier.
#  Although this script should only mess about with your current Python's site-packages, there is
#  absolutely no warranty. Use at your own risk, etc.
#  Why do this?
#     1. Installing the dependencies required for Zato on my laptop is not desirable
#     2. I run Zato inside docker containers which do not have ssh exposed
#     3. Setting up a project with additional sources in PyCharm takes way too long
#  If your folder structure matches the following zato_extra_paths will also be added.
#     ./
#     ./zato_extra_paths/
#     ./scripts/
#  To use this script, run the following commands:
#     $ pyenv install 2.7.13
#     $ pyenv virtualenv 2.7.13 zato
#     $ echo zato > .python-version
#     $ pyenv which python
#     /Users/kbni/.pyenv/versions/zato/bin/python <-- this is the interpeter you add to your editor configuration
#     $ ./scripts/

set -e 

PROJECT_DIR="$(cd "$(dirname "$0")/.." && echo $PWD)"
PACKAGES="$(python -c 'import sys; [sys.stdout.write(d+"\n") for d in sys.path]' | grep '/site-packages' | head -n1)"

# Setup a fake site-packages
if [ ! -e "${PACKAGES}/.fake-zato" ]; then
    echo "Messin' about with your site-packages (${PACKAGES})"
    mv "${PACKAGES}" "${PACKAGES}-real"
    mkdir -p "${PACKAGES}"
    touch "${PACKAGES}/.fake-zato"

# Clear out our site-packages
for pkg_file in "${PACKAGES}"/*; do
    if [ "$(basename "$pkg_file")" = "zato" ]; then
        continue # Don't do anything with the 'zato' directory
    if [ ! -e "$pkg_file" ]; then
        continue # Didn't find files under site-packages/
    if [ -L "$pkg_file" ]; then
        # Remove any symbolic links, as we'll recreate these
        rm "${pkg_file}"
        # Move any real packages
        mv "${pkg_file}" "${PACKAGES}-real/"

# Link any packages from the real site-packages back to the fake
for pkg_file in "${PACKAGES}-real"/*; do
    if [ ! -e "$pkg_file" ]; then
        continue # Didn't find files under site-packages/
    ln -s "${pkg_file}" "${PACKAGES}/$(basename "$pkg_file")"

# Copy the zato package hierarchies into site-packages
if [ ! -e "${PACKAGES}/zato" ]; then
    if [ ! -e "${PACKAGES}/" ]; then
        wget "" -O "${PACKAGES}/"
    SOURCES_TEMP="$(mktemp -d)"
    mkdir -p "${PACKAGES}/zato"
    unzip -q "${PACKAGES}/" -d "${SOURCES_TEMP}"
    for src_pkg in "${SOURCES_TEMP}/zato-main/code"/*/src/zato; do
        if [ -d "$src_pkg" ]; then
            cp -R "${src_pkg}/" "${PACKAGES}/zato/"
    rm -rf "${SOURCES_TEMP}"

# Link our zato_extra_path libraries into site-packages
if [ -d "${PROJECT_DIR}/zato_extra_paths" ]; then
    if [ ! -e "$pkg_file" ]; then
        continue # Didn't find files under zato_extra_paths
    for pkg_file in "${PROJECT_DIR}/zato_extra_paths"/*; do
        dest_file="${PACKAGES}/$(basename "$pkg_file")"
        if [ ! -e "$dest_file" ]; then
            ln -s "${pkg_file}" "${dest_file}"

# List the site-packages
set -x 
ls -lah "${PACKAGES}"

Next: Beating a dead horse (2018-08-26)