Sublime Forum

Exit script without crashing sublime text

#1

It’s all in the title. I want to stop a plugin file, wherever I am.

In a normal script, exit() does the job, but in this case, it causes ST to freeze (needs a restart).

I’m aware of wrapping my whole code in a main function and call return when I want to stop, but it’d be cool if there was a proper solution.

0 Likes

#2

needing to do this probably means your script could be designed better.
for example, does your plugin subscribe to events with an EventListener? do you want to stop receiving those events as well? if so, maybe unload your plugin file, so that the script won’t run again until ST is restarted or the plugin file is modified… or if you don’t want to stop it completely, just raise an exception that won’t get caught and deal with the fact that there will be a traceback in the console…

side note: when using the Python build system, as opposed to a plugin, quit() seems to stop the script. But in a plugin file this results in

NameError: name 'quit' is not defined

0 Likes

#3

I presume you want to stop execution of your plugin code when some prerequisites aren’t satisfied (i.e. effectively prevent the plugin from being loaded in the first place).

Your only two options in this case are

  1. throwing an exception
  2. wrapping everything in a conditional block or function you can return out of

Depending on what you want to do, one is more appropriate than the other.

3 Likes

#4

@kingkeith Yes, quit() isn’t defined in a plugin, nor is exit(). You have to import sys and then sys.exit().

@FichteFoll The idea was to stop the script without having a dirty output in the console (a traceback), since in my case, it is going to be run every time ST starts. Is there a solution to raise an exception without having a full traceback… (seems to have found something here https://stackoverflow.com/questions/17784849/in-python-how-do-i-print-an-error-message-without-printing-a-traceback-and-clos)

0 Likes

#5

The code responsible for generating the trace back is in sublime_plugin.py; whenever it executes user code it catches any unhandled exceptions and then displays the trace back.

You could modify your copy of sublime_plugin to not display the trace back when exceptions are caught (unwise for a variety of reasons and probably not something you’re going to get your package users to do).

Another way to go would be to wrap the top level of your code in an exception handling block that eats the exception:

import sublime
import sublime_plugin

class ExampleCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        try:
            raise ValueError()
        except:
            pass

Or if you had a bunch of commands that you wanted to do this for, you could delegate to a parent class to do the exception handling for you so you only have to do it once and it works everywhere transparently:

import sublime
import sublime_plugin

class ExceptionCatchingTextCommand(sublime_plugin.TextCommand):
    def run_(self, edit_token, args):
        try:
            super().run_(edit_token, args)
        except:
            print("Not on my watch!")

class ExampleCommand(ExceptionCatchingTextCommand):
    def run(self, edit, item):
        print("Example says %s" % item)
        item = item + 12
        raise ValueError()
        print("Unprinted")

However in this case, you could just call return from your command or event listener and structure your code accordingly (like returning a boolean “no more runtime for you” for example).

More to the point, masking exceptions means that exceptions are masked. So for the second example, call the command without an item argument and all you see in the console is “Not on my watch!”. If you actually pass an argument, you see it printed, along with the text “Not on my watch!”. What made it say that? Hard to say (for more complicated code); there’s no exception trace back to tell you where and what went wrong.

If your plugin is doing stuff only when Sublime starts (or you want to stop it from originally loading), perhaps you could implement plugin_loaded and invoke your top level logic from there, or set a flag that commands and event listeners can check to see if they should do anything or not.

0 Likes

#6

If the script being stopped is not the desired behavior, I don’t see an issue with having an exception (and a traceback) printed onto the console. That way it is more likely for users to spot that something is going wrong.

If the script being stopped is a valid alternative that still leaves the script usable, I suggest my second option of wrapping everything in a function.

2 Likes

#7

Stopping the script is the desired behavior, and I don’t want a traceback since I want to use this stop feature every time ST starts, and nothing was wrong.

I guess I should have explain the whole problem first, it would have been clearer:

It’s for my plugin kpymap, I wanted to have the ability to use options. So, I wanted to use the settings, the problem being that the setting aren’t able when the plugin is first loaded by Sublime Text (since it’s before plugin_loaded is called).

I didn’t wanted to do this, since it would have meant to add more boilerplate to the kpymaper.py, something like this:

from kpymap.kpymap import *
reset()

SHOULD_RUN = False

def main():
    # all the code goes here

def plugin_loaded():
    SHOULD_RUN = True

if SHOULD_RUN:
    main()

So, what I’ve done is basically created my own little setting system (not like the sublime text one, it’s basically a dict that you can change the values only from kpymaper.py). This way I don’t need to wait for the ST settings to be ready.

But thanks anyway for your quick answer, I appreciate it :slight_smile:

Matt

0 Likes