title: Is there any way to stop pycurl perform() execution?
body: <p>I'm currently working on a simple multi-threaded downloader with pycurl and python, now I can pause the download if I want but that simply eats too much memory and if the process is killed I cannot directly resume it. So I came up with a workaround (not implemented yet) to stop the download, save the byte position (which I can get with the progress function) to a XML file and then continue the downloads from there by specifying said byte position.</p>
  <p>Where I'm stuck at is I somehow need to close the connection while perform() is executing, since perform() is blocking I thought I can call close() on it from another thread but that just throws an exception. I cannot seem to find a single thing in either pycurl or libcurl documentation which can help me accomplish what I want.</p>
  <p>As to why I'm using pycurl over the simpler requests, I've been using pycurl for basic automation for a while now and am just used to that, if there's no other way here to do what I want with pycurl then I guess requests might be the last resort.</p>
  <pre class="lang-py prettyprint-override"><code>class Downloader:
      def __init__(self, url, parts):
          self.url = url
          self.fileName = re.search(r&quot;(?:[^/][\d\w.]+)+$&quot;, self.url, flags=re.IGNORECASE).group(0)
          self.parts = parts
          self.pause = False
          self.fileSize = round(self._getSize())
          self.partSize = round(self.fileSize / self.parts)
          self.threads = list()
          self.curlObjs = list()
  
      # Get file size by only downloading the HEADER and then calling getinfo for the length.
      def _getSize(self):
          curl = pycurl.Curl()
          curl.setopt(curl.URL, self.url)
          curl.setopt(curl.FOLLOWLOCATION, True)
          curl.setopt(curl.NOBODY, True)
          curl.perform()
          fileSize = curl.getinfo(curl.CONTENT_LENGTH_DOWNLOAD)
          curl.close()
          return fileSize
  
      # Track individual file part download progress.
      def _trackProgress(self, totalDown, currentDown, totalUp, currentUp):
          pass  # TODO
          # if currentDown != 0 and currentDown == totalDown:
              # print(f&quot;Download Completed!\n{currentDown}/{totalDown}&quot;)
  
      # Calculate the part size, execute _downloadRange in separate threads, merge file parts on download completion.
      def download(self):
          partStart = 0
          partEnd = self.partSize
  
          for part in range(1, self.parts + 1):
              t = threading.Thread(target=self._downloadRange, args=(partStart, partEnd, part))
              self.threads.append(t)
              t.start()
              partStart += self.partSize + 1 if part == 1 else self.partSize
              partEnd += self.partSize
  
          for t in self.threads:
              t.join()
  
          self._mergeFiles(self.fileName)
  
      # Download the specified range and write it to a file part.
      def _downloadRange(self, startRange, endRange, fileNo):
          with open(f&quot;{self.fileName}{fileNo}.part&quot;, &quot;wb&quot;) as f:
              curl = pycurl.Curl()
              self.curlObjs.append(curl)
              curl = curlObj.curl
              curl.setopt(curl.URL, self.url)
              curl.setopt(curl.FOLLOWLOCATION, True)
              curl.setopt(curl.RANGE, f&quot;{startRange}-{endRange}&quot;)
              curl.setopt(curl.WRITEDATA, f)
              curl.setopt(curl.NOPROGRESS, False)
              curl.setopt(curl.XFERINFOFUNCTION, self._trackProgress)
              curl.perform()
              curl.close()
  
      # Merge the file parts into one and delete the parts.
      def _mergeFiles(self, fileName):
          with open(fileName, &quot;wb&quot;) as o:
              for part in range(1, self.parts + 1):
                  with open(f&quot;{self.fileName}{part}.part&quot;, &quot;rb&quot;) as p:
                      o.write(p.read())
                  os.remove(f&quot;{self.fileName}{part}.part&quot;)```
  </code></pre>