Add A Post-Scan Plugin

Built-In vs. Optional Installation


Some post-scan plugins are installed when ScanCode itself is installed, e.g., the License Policy Plugin, whose code is located here:

These plugins do not require any additional installation steps and can be used as soon as ScanCode is up and running.


ScanCode is also designed to use post-scan plugins that must be installed separately from the installation of ScanCode. The code for this sort of plugin is located here:

This wiki page will focus on optional post-scan plugins.

Example Post-Scan Plugin: Hello ScanCode

To illustrate the creation of a simple post-scan plugin, we’ll create a hypothetical plugin named Hello ScanCode, which will print Hello ScanCode! in your terminal after you’ve run a scan. Your command will look like something like this:

scancode -i -n 2 <path to target codebase> --hello --json <path to JSON output file>

We’ll start by creating three folders:

  1. Top-level folder – /scancode-hello/

  2. 2nd-level folder – /src/

  3. 3rd-level folder – /hello_scancode/

1. Top-level folder – /scancode-hello/

  • In the /scancode-toolkit/plugins/ directory, add a folder with a relevant name, e.g., scancode-hello. This folder will hold all of your plugin code.

  • Inside the /scancode-hello/ folder you’ll need to add a folder named src and 7 files.

  1. /src/ – This folder will contain your primary Python code and is discussed in more detail in the following section.

The 7 Files are:

  1. .gitignore – See, e.g., /plugins/scancode-ignore-binaries/.gitignore

  1. apache-2.0.LICENSE – See, e.g., /plugins/scancode-ignore-binaries/apache-2.0.LICENSE


graft src

include setup.cfg
include .gitignore
include NOTICE
include apache-2.0.LICENSE

global-exclude *.py[co] __pycache__ *.*~
  1. NOTICE – See, e.g., /plugins/scancode-ignore-binaries/NOTICE


  3. setup.cfg

license_file = NOTICE

universal = 1

release = clean --all  bdist_wheel
  1. – This is an example of what our file would look like:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-

from __future__ import absolute_import
from __future__ import print_function

from glob import glob
from os.path import basename
from os.path import join
from os.path import splitext

from setuptools import find_packages
from setuptools import setup

desc = '''A ScanCode post-scan plugin to to illustrate the creation of a simple post-scan plugin.'''

    license='Apache-2.0 with ScanCode acknowledgment',
    package_dir={'': 'src'},
    py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')],
        # complete classifier list:
        'Development Status :: 4 - Beta',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: Apache Software License',
        'Programming Language :: Python',
        'Programming Language :: Python :: 3',
        'Topic :: Utilities',
        'scancode', 'plugin', 'post-scan'
        'scancode_post_scan': [
            'hello = hello_scancode.hello_scancode:SayHello',

2. 2nd-level folder – /src/

  1. Add an file inside the src folder. This file can be empty, and is used to indicate that the folder should be treated as a Python package directory.

  2. Add a folder that will contain our primary code – we’ll name the folder hello_scancode. If you look at the example of the file above, you’ll see this line in the entry_points section:

'hello = hello_scancode.hello_scancode:SayHello',
  • hello refers to the name of the command flag.

  • The first hello_scancode is the name of the folder we just created.

  • The second hello_scancode is the name of the .py file containing our code (discussed in the next section).

  • SayHello is the name of the PostScanPlugin class we create in that file (see sample code below).

3. 3rd-level folder – /hello_scancode/

  1. Add an file inside the hello_scancode folder. As noted above, this file can be empty.

  2. Add a file.


from plugincode.post_scan import PostScanPlugin
from plugincode.post_scan import post_scan_impl
from scancode import CommandLineOption
from scancode import POST_SCAN_GROUP

Create a PostScanPlugin class

The PostScanPlugin class PostScanPlugin code) inherits from the CodebasePlugin class (see CodebasePlugin code), which inherits from the BasePlugin class (see BasePlugin code).

class SayHello(PostScanPlugin):
    Illustrate a simple "Hello World" post-scan plugin.

    options = [
        is_flag=True, default=False,
        help='Generate a simple "Hello ScanCode" greeting in the terminal.',

    def is_enabled(self, hello, **kwargs):
        return hello

    def process_codebase(self, codebase, hello, **kwargs):
        Say hello.
        if not self.is_enabled(hello):

        print('Hello ScanCode!!')

Load the plugin

  • To load and use the plugin in the normal course, navigate to the plugin’s root folder (in this example: /plugins/scancode-hello/) and run pip install . (don’t forget the final .).

  • If you’re developing and want to test your work, save your edits and run pip install -e . from the same folder.

More-complex examples

This Hello ScanCode example is quite simple. For examples of more-complex structures and functionalities you can take a look at the other post-scan plugins for guidance and ideas.

One good example is the License Policy post-scan plugin. This plugin is installed when ScanCode is installed and consequently is not located in the /plugins/ directory used for manually-installed post-scan plugins. The code for the License Policy plugin can be found at /scancode-toolkit/src/licensedcode/ and illustrates how a plugin can be used to analyze the results of a ScanCode scan using external data files and add the results of that analysis as a new field in the ScanCode JSON output file.