There are a few issues with this code. The first is that as presented above, the code doesn’t compile because the
self.replace() call in
handle_thread() isn’t indented properly, though I’m assuming that should line up with the
With that change in mind, if you follow the logic of the code as it’s laid out the reason that you’re seeing this error is because the call to
self.handle_thread() in the
lambda call is broken. Notice that the argument list for
def handle_thread(self, edit, threads, offset = 0, i = 0, dir = 1):
However at the call point:
lambda: self.handle_thread(threads, offset, i, dir)
That is, you’ve skipped the
edit argument in the call. Thus the result of this call is that every argument you provide is interpreted differently than you expect;
edit becomes the value
threads becomes the value
offset becomes the value
i becomes the value
dir is set to the default of
1 because it’s not provided.
With that in mind, once the first call to
set_timeout() happens and
handle_thread() is called again, the value of
threads is 0; the
if test concludes that it doesn’t need to do anything further, and the code falls through to the
self.replace() call, where it provides the value
0 for the argument of threads.
Now it’s easy to see that in
replace(), the line in question gets mad because it’s expecting that value to be an instance of
Thread but it’s actually a number, and kabooom.
The fix in that case would be to include the
edit argument as the first argument in the
lambda call, so that all of the arguments line up the way you expect them to.
If you make that change, you’ll find that the command never actually completes. That’s because of this line:
It looks like the intention is that this should return
True while the thread is running and
False when it’s not, so that once the request completes the results can be handled. The
Thread class doesn’t (AFAIK but I’m no guru) work that way; what this is actually testing is whether or not the value is not
None, which it always is.
if you were to include a line like
print(threads) at the top of of
handle_thread() you can see that the state of the thread changes from
stopped when the request finishes, but that’s not the state that you’re checking.
So that line should look more like:
Now the command will correctly drop out of the loop when the result comes back, but there’s a new issue instead:
Traceback (most recent call last):
File "/home/tmartin/.config/sublime-text-3/Packages/User/test_panel_thing.py", line 28, in <lambda>
sublime.set_timeout(lambda: self.handle_thread(edit, threads, offset, i, dir), 100)
File "/home/tmartin/.config/sublime-text-3/Packages/User/test_panel_thing.py", line 33, in handle_thread
File "/home/tmartin/.config/sublime-text-3/Packages/User/test_panel_thing.py", line 37, in replace
for todo in thread.result():
TypeError: 'list' object is not callable
There are a couple of bugs here; the first is that if there was an error collecting the JSON in the thread, it sets the value to
False to indicate that, but this code isn’t checking for it. The other is that
result isn’t a function, it’s the actual result of the call (which in this case is a list).
Based on that, the code could perhaps be expressed better as the following:
def replace(self, edit, thread):
todos = 
for todo in thread.result:
sublime.status_message("Error during request")
Now a test is done to see if there is a result or not; if not a message is displayed in the status bar, but otherwise the result of the command is that a quick panel opens as you might expect:
One last thing to note is that in Sublime Text 2 (which is the version the tutorial you’re referencing is from) a plugin was free to create it’s own
edit object as desired. However in Sublime Text 3 this is strictly controlled and the only thing that can provide an
edit object is Sublime itself, which it passes to the
run() method of a
The value of the
edit object is only valid from inside of the
run() call that it was given to. If you try to keep it and use it later, you’ll get an error as a result.
That doesn’t matter for the code as you have it laid out here because it’s not actually trying to change any text. However if your ultimate goal is to use the result of the call to modify the text, you can no longer do it this way.
Instead, you would need to gather the text and then pass it as an argument to a
run_command() so that Sublime will execute that command and it can then make the change for you. Depending on your use case you can use the
insert command that already exists to do this, or you can create one of your own.
This is outlined a bit in the plugin porting guide.