Python Development

Qip provide a few features to ease the development process.

Editable mode

Qip provides an editable mode to allow for rapid development when developing a Python package:

>>> qip install -e .

Let’s clone the popular Pystache repository to demonstrate this feature:

>>> git clone https://github.com/defunkt/pystache.git
>>> cd ./pystache
>>> qip install -e .

The pystache definition will ensure that the install-location value will target the cloned repository:

{
    "identifier": "pystache",
    "version": "0.5.4",
    "namespace": "library",
    "description": "Mustache for Python",
    "command": {
        "pystache": "python -m pystache.commands.render",
        "pystache-test": "python -m pystache.commands.test"
    },
    "environ": {
        "PYTHONPATH": "${INSTALL_LOCATION}:${PYTHONPATH}"
    },
    "variants": [
        {
            "identifier": "3.8",
            "install-location": "/path/to/pystache/",
            "requirements": [
                "python >= 3.8, < 3.9"
            ]
        }
    ]
}

It is then possible to execute the following command:

>>> wiz -add /tmp/qip/definitions run pystache -- 'Hello {{world}}' '{"world": "everybody"}'
info: Start command: python -m pystache.commands.render 'Hello {{world}}' '{"world": "everybody"}'
Hello everybody

You can modify the pystache/commands/render.py file to print a different output and ensure that it works as expected:

diff --git a/pystache/commands/render.py b/pystache/commands/render.py
index 1a9c309..9738c5e 100644
--- a/pystache/commands/render.py
+++ b/pystache/commands/render.py
@@ -88,7 +88,7 @@ def main(sys_argv=sys.argv):
         context = json.loads(context)

     rendered = renderer.render(template, context)
-    print rendered
+    print "test " + rendered

The command will then return:

>>> wiz -add /tmp/qip/definitions run pystache -- 'Hello {{world}}' '{"world": "everybody"}'
info: Start command: python -m pystache.commands.render 'Hello {{world}}' '{"world": "everybody"}'
test Hello everybody

Optional dependencies

Qip supports extra keywords provided in the request. Many Python packages use this installation feature, like Gunicorn.

>>> qip install gunicorn[gevent]

When using extra keywords, the nature of the package installed is fundamentally modified. Not only does it change the dependencies, but it could also change the Entry-Points created.

To prevent name clashes, the Wiz definition extracted will include the list of sorted extra keywords used within its identifier:

{
    "identifier": "gunicorn-event",
    ...
}

Custom Wiz definition

A default Wiz definition will be created for each Python packages to install. It will contain:

It is possible to add a custom definition within the repository to extend this Wiz definition. The custom definition should be included in the source code under package_data/wiz.json.

Let’s use again the Pystache repository to demonstrate this feature:

>>> git clone https://github.com/defunkt/pystache.git
>>> cd ./pystache

Add the following definition in pystache/package_data/wiz.json

{
    "command": {
        "say_hello": "python -m pystache.commands.render 'Hello {{world}}' '{\"world\": \"everybody\"}'"
    }
}

Now install the definition as follows:

>>> qip install .

It is then possible to execute the following command:

>>> wiz -add /tmp/qip/definitions run say_hello
info: Start command: python -m pystache.commands.render 'Hello {{world}}' '{"world": "everybody"}'
Hello everybody

Using a custom definition could be particularly helpful when a Python package depends on a non-Python library.

Note

It is also possible to add optional custom definition matching extra keywords provided in the request. For instance, if a non-Python library must be set as a dependency when the “gevent” keyword is passed when installing the Gunicorn library, a targeted definition can be set in gunicorn/package_data/wiz-gevent.json.

Working with DCCs

When writing a Python plugin for a Digital content creation tool, a custom Wiz definition should be used to ease the development and deployment process.

Here are a few usage examples:

{
    "identifier": "foo",
    "namespace": "maya",
    "environ": {
        "MAYA_PLUG_IN_PATH": "${INSTALL_LOCATION}/foo/package_data/maya/plugin:${MAYA_PLUG_IN_PATH}",
        "MAYA_SCRIPT_PATH": "${INSTALL_LOCATION}/foo/package_data/maya/script/mel:${MAYA_SCRIPT_PATH}",
        "PYTHONPATH": "${INSTALL_LOCATION}/foo/package_data/maya/script/python:${PYTHONPATH}"
    },
    "requirements": [
        "maya"
    ]
}
{
    "identifier": "foo",
    "namespace": "flame",
    "environ": {
        "DL_PYTHON_HOOK_PATH": "${INSTALL_LOCATION}/foo/package_data/python:${DL_PYTHON_HOOK_PATH}"
    },
    "requirements": [
        "flame"
    ]
}
{
    "identifier": "foo",
    "namespace": "nuke",
    "environ": {
        "NUKE_PATH": "${INSTALL_LOCATION}/foo/package_data/nuke:${NUKE_PATH}"
    },
    "requirements": [
        "nuke"
    ]
}
{
    "identifier": "foo",
    "namespace": "houdini",
    "environ": {
        "HOUDINI_PATH": "${INSTALL_LOCATION}/foo/package_data/houdini:${HOUDINI_PATH}"
    },
    "requirements": [
        "houdini"
    ]
}
{
    "identifier": "foo",
    "namespace": "rv",
    "environ": {
        "RV_SUPPORT_PATH": "${INSTALL_LOCATION}/foo/package_data/rv:${RV_SUPPORT_PATH}"
    },
    "requirements": [
        "rv"
    ]
}