Write custom extension scripts using the Code42 CLI and py42

While the Code42 CLI aims to provide an easy way to automate many common Code42 tasks, there will likely be times when you need to script something the CLI doesn’t have out-of-the-box.

To accommodate for those scenarios, the Code42 CLI exposes a few helper objects in the code42cli.extensions module that make it easy to write custom scripts with py42 that use features of the CLI (like profiles) to reduce the amount of boilerplate needed to be productive.

Before you begin

The Code42 CLI is a python application written using the click framework, and the exposed extension objects are custom click classes. A basic knowledge of how to define click commands, arguments, and options is required.

The sdk_options decorator

The most important extension object is the sdk_options decorator. When you decorate a command you’ve defined in your script with @sdk_options, it will automatically add --profile and --debug options to your command. These work the same as in the main CLI commands.

Decorating a command with @sdk_options also causes the first argument to your command function to be the state object, which contains the initialized py42 sdk. There’s no need to handle user credentials or login, the sdk_options does all that for you using the CLI profiles.

The script group

The script object exposed in the extensions module is a click.Group subclass, which allows you to add multiple sub-commands and group functionality together. While not explicitly required when writing custom scripts, the script group has logic to help handle and log any uncaught exceptions to the ~/.code42cli/log/code42_errors.log file.

If only a single command is added to the script group, the group will default to that command, so you don’t need to explicitly provide the sub-command name.

An example command that just prints the username and ID that the sdk is authenticated with:

import click
from code42cli.extensions import script, sdk_options

@click.command()
@sdk_options
def my_command(state):
    user = state.sdk.users.get_current()
    print(user["username"], user["userId"])

if __name__ == "__main__":
    script.add_command(my_command)
    script()

Ensuring your script runs in the Code42 CLI python environment

The above example works as a standalone script, if it were named my_script.py you could execute it by running:

python3 my_script.py

However, if the Code42 CLI is installed in a different python environment than your python3 command, it might fail to import the extensions.

To workaround environment and path issues, the CLI has a --python option that prints out the path to the python executable the CLI uses, so you can execute your script with$(code42 --python) script.py on Mac/Linux or &$(code42 --python) script.py on Windows to ensure it always uses the correct python path for the extension script to work.

Installing your extension script as a Code42 CLI plugin

The above example works as a standalone script, but it’s also possible to install that same script as a plugin into the main CLI itself.

Assuming the above example code is in a file called my_script.py, just add a file setup.py in the same directory with the following:

from distutils.core import setup

setup(
    name="my_script",
    version="0.1",
    py_modules=["my_script"],
    install_requires=["code42cli"],
    entry_points="""
        [code42cli.plugins]
        my_command=my_script:my_command
    """,
)

The entry_points section tells the Code42 CLI where to look for the commands to add to its main group. If you have multiple commands defined in your script you can add one per line in the entry_points and they’ll all get installed into the Code42 CLI.

Once your setup.py is ready, install it with pip while in the directory of setup.py:

$(code42 --python) -m pip install .

Then running code42 -h should show my-command as one of the available commands to run!