Full Reference

Introduction

On this page, the debugging functionality will be documented in its entirely:

  1. "UIP DEBUG CONFIGURATIONS" View
  2. Plugin Dependencies
  3. configurations.yml
  4. Universal Extension API Levels
  5. Launching/Debugging
  6. Output Only Fields and Publishing Events
  7. Output Channels
  8. Known Limitations

1 - "UIP DEBUG CONFIGURATIONS" View

The “UIP DEBUG CONFIGURATIONS” view located in the “Run and Debug” section is responsible for showing all the launchable configurations defined in configurations.yml:

Initially, configurations.yml will not be present, so the view will report that and show a button to “Add Configurations”:

If there are any issues with configurations.yml, the view will report that and show a button to “See Problems”:

If there are no problems and there is at least 1 valid configuration, the view will render the contents of configurations.yml (layout will differ depending on the structure of your configurations.yml):

Hovering on each configuration entry should expose the following two icons:

Clicking on the “star” icon will mark the selected configuration entry as the default one (Clicking again will deselect it). This will be indicated with a green circle to the left of the configuration entry name:

The default configuration entry will persist even if after VSCode has been closed. Clicking on the “debug” icon (next to the “star” icon) will launch that specific entry.

To update the list of entries in the "UIP DEBUG CONFIGURATIONS” view, click the “refresh” icon:

Clicking the “refresh” icon, however, should not be necessary. The view will automatically be updated when

  • configurations.yml changes

  • template.json changes (during UIP: Pull)

2 - Plugin Dependencies

The debugging functionality has two dependencies: debugpy PIP module, and Universal Extension Bundle (universal_extension_bundle_1.2.0.zip).

  • debugpy module is required for the Python debugger on VSCode’s end (client) to connect to the debugging server on the Extension side.

  • Universal Extension Bundle is a proprietary zip file which contains the Universal Extension Base Class code for all the supported API levels mentioned in the requirements section. Specifically, the bundle zip consists of a folder called bundle which has:

    • universal_extension_1.0.0.zip

    • universal_extension_1.1.0.zip

    • universal_extension_1.2.0.zip

    • universal_extension_1.3.0.zip

The plugin has logic to install and setup both of the dependencies. For the bundle, however, additional steps are required on the user’s end.

2.1- Installing debugpy

If the plugin detects debugpy is not installed in the active Python interpreter, it will go ahead and prompt the user to install it:

2.2 - Installing universal_extension_bundle_1.2.0.zip

The bundle zip is extracted and stored in:

  • C:\Users\<USER>\AppData\Roaming\Code\User\globalStorage\stonebranch.uip (Windows)

  • /home/<USER>/.vscode-server/data/User/globalStorage/stonebranch.uip (WSL using remote access)

  • /home/<USER>/.config/Code/User/globalStorage/stonebranch.uip (Linux/Unix)

Once extracted, there should be a folder called bundle in stonebranch.uip folder with all the zip files for each of the API versions. Note, the bundle only needs to be installed once.

If the plugin detects the bundle is not present in stonebranch.uip, the plugin will show the following prompt:

If the user selects Yes, then a dropdown will be shown to select the installation source.

  • If the user selects Path to universal_extension_bundle_1.2.0.zip, a file picker dialog will show up and let the user select only a zip file.

  • If the user selects URL, they can specify a download link to the bundle zip. Upon selecting URL, 3 additional inputs will be shown:

    • One for the URL that must start with either http or https.

    • One for the username, if applicable. Can be empty.

    • One for the password/token, if applicable. Can be empty. User input will be masked out.

The plugin will attempt to download the file from the URL. If the URL does not point to a zip file, it will issue an error.

  • If the user selects Gitlab Package Registry, they will need to specify a full URL to the project/repository whose Package Registry contains the bundle zip. For instance, assuming that the user has a project/repository called DummyProject,
    1. Upload the universal_extension_bundle_1.2.0.zip as a generic package to DummyProject's package registry using the steps outline in GitLab Generic Packages Repository | GitLab. If successful, you should see something like this:


      Note that
      shrey_test is a group, DummyProject is a project/repository. In the image above,
      - #1 shows the package name. This does not have to be
      universal_extension_bundle, but it makes sense to name it that, and it makes the job of finding the actual zip easier for the plugin.
      - #2 shows the package version. Once again, it doesn’t have to be
      1.2.0, but it makes sense to keep it that.
      - #3 shows the actual, uploaded bundle zip. This must be named
      universal_extension_bundle_1.2.0.zip
    2. Assuming Gitlab Package Registry was clicked from the dropdown menu, the next prompt should be to enter the project/repository URL. This is the URL that takes the user to the project/repository’s landing page:

    3. Then the next prompt should ask for the Personal Access Token.

    4. Depending on how the package was uploaded in step 1, two things can happen:

      1. The plugin can find the universal_extension_bundle_1.2.0.zip given that the package name is universal_extension_bundle (#1 in Figure 49) and package version is 1.2.0 (#2 in Figure 49)

      2. The plugin cannot find the bundle zip because the package name and version are something other than universal_extension_bundle and 1.2.0. If so, the plugin will show another dropdown with all the packages, and the user must select the one that contains universal_extension_bundle_1.2.0.zip

Assuming the zip file is available, the plugin will verify it using checksums before proceeding further. This is the default, but the user can turn off checksum verification in the settings:

Even if the verification is turned off, the plugin will still ensure the zip file consists of the bundle folder, which itself should contain the files mentioned previously.

2.3 - Uses of universal_extension_bundle_1.2.0.zip

Aside from allowing the debugger to accurately launch/debug Universal Extension tasks, the bundle zip also adds autocompletion/suggestion functionality to extension.py. For example, without the bundle installed, VSCode will complain that it cannot find the universal_extension module:

With the bundle installed,

3 - configurations.yml

configurations.yml (located in .uip/debug/configurations.yml) is what the developer uses to define their debug configurations. At the top level, it consists of two objects:

  • properties (optional)

  • api (required)

The properties object is used to define “global” options that apply to all debug configurations. As of now, it only consists of the agent object which only has the log_level property.

The api object is where the developer actually defines the debug configurations. Specifically, they can add configurations for:

  • Dynamic choice commands using dynamic_choice_commands object

  • Extension Start using extension_start object


dynamic_choice_commands is an object where the key is the name of a dynamic choice field and the value is an array of configuration entries.

For instance, in the figure below, dynamic_choice_commands consists of the exclude_file_ext and file dynamic choice fields. exclude_file_ext is a dynamic choice field with two debug entries (can have any number of entries >=1) uniquely identified by the name property.


extension_start is simply an array of configuration entries:


Each debug configuration entry must have the name property, and it must be unique.

The log_level property is optional, and by default, it will have a value of Inherited which means it will inherit the value from the agent's log_level defined in the properties object.

For extension_start, the fields object is required if there is at least 1 field defined. For a dynamic choice field under dynamic_choice_commands, the fields object is required if it has at least 1 dependent field.


extension_start has some additional properties besides name, log_level, and fields that are useful. It has:

  • runtime_dir which can be used to set the runtime directory for the Extension instance. By default, the runtime directory is the currently opened VSCode workspace.

  • env_vars which can be used to define environment variables that are available in extension_start() method (can be accessed using os.environ).

    • example:

  • uip object which maps to the self.uip object introduced in 7.2.0.0. The uip object consists of:
    • is_triggered boolean property with an initial value of false. The developer can access this using self.uip.is_triggered. If it is set to true, then in extension_start(), the following additional values will be available for access:

      • self.uip.trigger_id which is a randomly generated uuid

      • self.uip.monitor_id which is a randomly generated uuid

    • The self.uip object also has an instance_id property (accessed using self.uip.instance_id) which is always available. Once again, it is a randomly generated uuid.

      The self.uip object is only available for Universal Extension API levels 1.2.0 and greater. For levels <= 1.1.0, the self.uip object will not be available.

    • variables object can be used to define task variables. The variables can be accessed using self.uip.task_variables dictionary.
      • example:


        Note that:
        • the type of the value of each variable is string. In other words, self.uip.task_variables['var3'] will return '[4,5,6]' which is a string and NOT a list. This is enforced to keep the behavior consistent with how the controller passes in task variables.

        • if variables object is not defined, the task_variables dictionary will not be made available UNLESS template.json has the sendVariables property set to Local in which case an empty dictionary will be passed in for task_variables.


In addition to supporting autocompletion as shown in the demo, configurations.yml also supports type checking (e.g. an integer value specified for a Text field will be flagged as an error).

Hovering over a field will reveal some information about it. See the example below:

configurations.yml supports all field types that the Controller supports. This includes:

  • Plain text field

  • Yaml text field

  • Json text field

  • Large text field

  • Credential field

  • Script field

    • Essentially a text field that accepts the full path to a file. The developer is responsible for creating the script file.

  • Boolean field

  • Integer field

  • Float field

  • Choice fields (dynamic and non-dynamic)

  • Array fields

Here is an example below showing how to use the fields:

4 - Universal Extension API Levels

As shown in the demo, the developer can change the API Level they want to target using:

Clicking on UE API: 1.3.0 in the screenshot above will allow the developer to easily change the API Level. No additional change is required.

5 - Launching/Debugging

Assuming configurations.yml is correctly configured, and the plugin dependencies are installed, the developer can start debugging by simply pressing F5.

If a configuration entry has not been select as a default in the “UIP DEBUG CONFIGURATIONS” view, the following error will be issued:

and the view will be opened automatically.

Assuming an entry has been selected as the default, pressing F5 will start the debugging session as shown in the previous pages.

The VSCode Python Debug Client attempts to connect to the debugpy server injected in the Extension instance. The debugpy server listens on 127.0.0.1 port 5678. In most cases, this should be fine. If the developer is already using port 5678 for something else (or it’s being used by the OS/other program), the developer can change the port by modifying launch.json in the .vscode folder.

If a breakpoint has not been set, the debugger will start and finish without stopping. This is essentially the same as just simulating a launch.


The VSCode debug toolbar has also been enhanced to include a “Terminate” button:

This is used to force kill the Extension instance. This is useful if the Extension is stuck in an infinite loop (or in other blocking calls).


A “Cancel” button has also been added to simulate the Cancel command:

Clicking the “Cancel” button will call the extension_cancel method. Since the Cancel functionality does not apply to dynamic choice commands, it will not be shown when a dynamic choice command is being debugged. Furthermore, the Cancel functionality was not available in API Level 1.0.0, and thus, it will not be shown if the Universal Extension API Level in the Status Bar shows 1.0.0.

6 - Output Only Fields and Publishing Events

Output only fields can be updated as usual using the update_output_fields method from the ui module (or using self.update_extension_status method). For example,

Running extension_start() shown above will result in:

As you can see in the figure above, the status of all output only fields (when they are updated) is printed in the UIP (CONSOLE) output channel. UIP (CONSOLE) will update in real-time as the fields are updated.


Publishing events is a similar process. Running the following:

will result in

(Note that both the events were received about 3 seconds apart just as expected)

Even though publishing events is supported, it isn’t all that useful since the concept of triggers and monitor does not apply to the plugin. They can be useful to verify the event is successfully configured/setup.

7 - Output Channels

There are four output channels that the Extension sends output to:

  • UIP (STDOUT)

    • Shift+Alt+1 for quick access

  • UIP (STDERR)

    • Shift+Alt+2 for quick access

  • UIP (EXTENSION)

    • Shift+Alt+3 for quick access

  • UIP (CONSOLE)

    • Shift+Alt+4 for quick access

All four channels will be updated in real-time for all Universal Extension API Levels except 1.0.0 (Output will still be available for 1.0.0, but after the Extension finishes).

In API Level 1.0.0, the STDOUT and STDERR channels were not modified to flush output as soon as it was received. As a result, the output wasn’t available until the buffer was full or until the Extension process had finished.


As the name implies, UIP (STDOUT) will contain all output the Extension sends to STDOUT. This includes the output of all print statements.


As the name implies, UIP (STDERR) will contain all output the Extension sends to STDERR. This includes the output of the logger (e.g. logger.info/self.log.info etc.)


UIP (EXTENSION) will show the output of ExtensionResult returned by extension_start() or a dynamic choice command. All possible parameters of ExtensionResult will be rendered. For instance, the following

will result in


UIP (CONSOLE) will contain miscellaneous output that is still useful, but doesn’t fit in STDOUT/STDERR/EXTENSION. As shown in the previous section, it contains the output of Output Only Fields and Events.

8 - Known Limitations

8.1 - Uncaught Exceptions

While debugging, if an uncaught exception occurs (e.g. a = 1/0), VSCode sometimes opens the universal_extension.py base class file with some additional pop-ups. This can be prevented by unchecking the Uncaught Exceptions option in the Breakpoints section:

< Previous