I'm building a Sublime Text 3 plugin to shorten URLs using the goo.gl API. Bear in mind that the following code is hacked together from other plugins and tutorial code. I have no previous experience with Python. The plugin does actually work as it is. The URL is shortened and replaced inline. Here is the plugin code: import sublime import sublime_plugin import urllib.request import urllib.error import json import threading class ShortenUrlCommand(sublime_plugin.TextCommand): def run(self, edit): sels = self.view.sel() threads = [] for sel in sels: url = self.view.substr(sel) thread = GooglApiCall(sel, url, 5) # Send the selection, the URL and timeout to the class threads.append(thread) thread.start() # Wait for threads for thread in threads: thread.join() self.view.sel().clear() self.handle_threads(edit, threads, sels) def handle_threads(self, edit, threads, sels, offset=0, i=0, dir=1): next_threads = [] for thread in threads: sel = thread.sel result = thread.result if thread.is_alive(): next_threads.append(thread) continue if thread.result == False: continue offset = self.replace(edit, thread, sels, offset) thread = next_threads if len(threads): before = i % 8 after = (7) - before if not after: dir = -1 if not before: dir = 1 i += dir self.view.set_status("shorten_url", "[%s=%s]" % (" " * before, " " * after)) sublime.set_timeout(lambda: self.handle_threads(edit, threads, sels, offset, i, dir), 100) return self.view.erase_status("shorten_url") selections = len(self.view.sel()) sublime.status_message("URL shortener successfully ran on %s URL%s" % (selections, "" if selections == 1 else "s")) def replace(self, edit, thread, sels, offset): sel = thread.sel result = thread.result if offset: sel = sublime.Region(edit, thread.sel.begin() + offset, thread.sel.end() + offset) self.view.replace(edit, sel, result) return class GooglApiCall(threading.Thread): def __init__(self, sel, url, timeout): self.sel = sel self.url = url self.timeout = timeout self.result = None threading.Thread.__init__(self) def run(self): try: apiKey = "xxxxxxxxxxxxxxxxxxxxxxxx" requestUrl = "https://www.googleapis.com/urlshortener/v1/url" data = json.dumps({"longUrl": self.url}) binary_data = data.encode("utf-8") headers = { "User-Agent": "Sublime URL Shortener", "Content-Type": "application/json" } request = urllib.request.Request(requestUrl, binary_data, headers) response = urllib.request.urlopen(request, timeout=self.timeout) self.result = json.loads(response.read().decode()) self.result = self.result["id"] return except (urllib.error.HTTPError) as e: err = "%s: HTTP error %s contacting API. %s." % (__name__, str(e.code), str(e.reason)) except (urllib.error.URLError) as e: err = "%s: URL error %s contacting API" % (__name__, str(e.reason)) sublime.error_message(err) self.result = False The problem is that I get the following error in the console every time the plugin runs: Traceback (most recent call last): File "/Users/joejoinerr/Library/Application Support/Sublime Text 3/Packages/URL Shortener/url_shortener.py", line 51, in <lambda> sublime.set_timeout(lambda: self.handle_threads(edit, threads, sels, offset, i, dir), 100) File "/Users/joejoinerr/Library/Application Support/Sublime Text 3/Packages/URL Shortener/url_shortener.py", line 39, in handle_threads offset = self.replace(edit, thread, sels, offset) File "/Users/joejoinerr/Library/Application Support/Sublime Text 3/Packages/URL Shortener/url_shortener.py", line 64, in replace self.view.replace(edit, sel, result) File "/Applications/Sublime Text.app/Contents/MacOS/sublime.py", line 657, in replace raise ValueError("Edit objects may not be used after the TextCommand's run method has returned") ValueError: Edit objects may not be used after the TextCommand's run method has returned I'm not sure what the problem is from that error. I have done some research and I understand that the solution may be held in the answer to this question, but due to my lack of Python knowledge I can't figure out how to adapt it to my use case. Continue reading...