Sublime Forum

How to use Cancel Build when calling Default.exec.ExecCommand?

#1

I have a plugin that uses subprocesses to call out to another application and uses a custom build system to build the file that the user is working on. When the user builds the file using the build system, the Cancel Build command is greyed out. Is there something I can do to enable this? I looked in exec.py but didn’t see anything that would help.

Here is the class that inherits Default.exec.ExecCommand:

class ExecuteCondaEnvironmentCommand(Default.exec.ExecCommand, CondaCommand):
    """Override Sublime Text's default ExecCommand with a targeted build."""

    def run(self, **kwargs):
        """Run the current Python file with the conda environment's Python executable.
        The activated conda environment is retrieved from the Sublime Text
        window project data. The Python executable found in the conda
        environment's bin directory is used to build the file.
        """
        try:
            environment = self.project_data['conda_environment']
            
            if sys.platform == 'win32':
                executable_path = '{}\\python' .format(environment)
            else:
                executable_path = '{}/bin/python' .format(environment)

            kwargs['cmd'][0] = os.path.normpath(executable_path)
        except KeyError:
            pass

super(ExecuteCondaEnvironmentCommand, self).run(**kwargs)

And here is the build system that it uses:


{
    // target uses the ExecuteCondaEnvironmentCommand found in commands.py
    "target": "execute_conda_environment",
    "cmd": ["python", "-u", "$file"],
    "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
    "selector": "source.python"
}
0 Likes

#2

This is fairly curious indeed. According to the menus and the key bindings, the command for cancelling a build is cancel_build, but I can’t find any definition for that command anywhere in the Default package, which makes me think that it’s something that’s in the core. However, the exec command contains a definition for is_enabled:

def is_enabled(self, kill=False, **kwargs):
    if kill:
        return (self.proc is not None) and self.proc.poll()
    else:
        return True

Using the following minimalistic example of a command for a build target runs the build but does not enable the cancel button while the build is running:

class ExecuteCondaEnvironmentCommand(Default.exec.ExecCommand):
    def run(self, **kwargs):
        super(ExecuteCondaEnvironmentCommand, self).run(**kwargs)

Overriding the definition of Default.exec.ExecCommand.is_enabled to always returns True has the effect of making the Cancel Build command in the menu become enabled, but while building with the above target it doesn’t do anything when you select it.

From observation it looks as though the cancel_build command is a simple proxy for exec that invokes exec with an argument of kill set to True in order to cancel a process and which reports as disabled if exec thinks that it has nothing running.

As such the only method I can think of that would keep the cancel option enabled and operational during a build would be to launch the build using the exec command directly and not from a subclass of it. For example:

class ExecuteCondaEnvironmentCommand(sublime_plugin.WindowCommand):
    def run(self, **kwargs):
        self.window.run_command("exec", kwargs)

It’s not obvious from your example posted above, but is there a reason that you need to subclass Default.exec instead of just making a custom command that does the same manipulations and invokes exec manually?

2 Likes

#3

Thanks for digging in to this. I can’t remember why I chose to subclass Default, especially since I use self.window.run_command(‘exec’, …) elsewhere in the code. I’ll change the execute command to use run_command and see if it works as I expect.

Thanks again!

0 Likes

#4

In response to this, I did some experiments earlier today with a small hint from wbond.

A cancel key may be specified in build systems, expecting a command name. That command is being called with the same arguments as the target command, including cancel itself.
I did not check how is_enabled works with this, but I imagine returning false from it would prevent the cancel command from being run.

1 Like