2012-05-04 11 views
6

Sto scrivendo uno script Python utilizzando pycurl per consumare Twitter's Sreaming API. Ecco un breve frammento che fa esattamente questo (semplicemente mettere il vostro login Twitter/password per testarlo):Come gestire KeyboardInterrupt (Ctrl-c) bene con pycurl?

import pycurl 

user = 'USER' 
password = 'PWD' 

def handleData(data): 
    print(data) 

conn = pycurl.Curl() 
conn.setopt(pycurl.USERPWD, "%s:%s" % (user, password)) 
conn.setopt(pycurl.URL, 'https://stream.twitter.com/1/statuses/sample.json') 
conn.setopt(pycurl.WRITEFUNCTION, handleData) 
conn.perform() 

Il problema è che perché lo script consuma un ruscello, conn.perform() mai restituisce (o molto raramente). Pertanto, a volte ho bisogno di interrompere lo script e lo KeyboardInterrupt viene catturato dal metodo perform().

Tuttavia, non gestisce bene, stampa un brutto errore e genera un'eccezione diversa.

^CTraceback (most recent call last): 
    File "test.py", line 6, in handleData 
    def handleData(data): 
KeyboardInterrupt 
Traceback (most recent call last): 
    File "test.py", line 12, in <module> 
    conn.perform() 
pycurl.error: (23, 'Failed writing body (0 != 2203)') 

Il cURL FAQ dice che per interrompere un trasferimento in corso, una delle funzioni di callback (nel mio caso handleData) dovrebbe restituire un valore speciale. Questo è ottimo, ma lo KeyboardInterrupt non viene catturato da nessuna delle funzioni di callback!

Come posso fare questo ordinatamente?

EDIT: So che è possibile intercettare le eccezioni, ma pycurl fa ancora alcune cose divertenti:

Se lo faccio:

try: 
    conn.perform() 
except BaseException as e: 
    print('We caught the exception') 
    print(type(e)) 

ottengo:

^CTraceback (most recent call last): 
    File "test.py", line 6, in handleData 
    def handleData(data): 
KeyboardInterrupt 
We caught the exception 
<class 'pycurl.error'> 

Ciò significa che internamente, pycurl esegue un qualche tipo di rilevamento, stampa un brutto messaggio di errore e quindi solleva uno pycurl.error.

+0

'KeyboardInterrupt' [non è una sottoclasse di' Exception', è una sottoclasse di 'BaseException'] (http://docs.python.org/library/exceptions.html#exception-hierarchy) - tuttavia, ciò non modifica l'output. (Anche se un altro esempio del perché catturare l'eccezione è una cattiva idea.) –

+0

Grazie per averlo indicato, ho corretto il mio esempio. Non cambia la mia domanda però. – Wookai

+0

È necessario conn.close() dopo conn.perform(). –

risposta

1

È possibile eseguire questa operazione rilevando il tipo pycurl.error. Es:

try: 
    conn.perform() 
except pycurl.error, e: 
    errorCode, errorText = e.args 
    print 'We got an error. Code: %s, Text:%s'%(errorCode, errorText) 
+0

Questa è la stessa cosa della seconda cosa che ho provato, che non rimuove la stampa KeyboardInterrupt. – Wookai

2

devi catturare CTRL + C e di processo che segnalano
originale: Example 1
originale: Example 2


Esempio 1

#!/usr/bin/env python 
import signal 
import sys 
def signal_handler(signal, frame): 
     print 'You pressed Ctrl+C!' 
     sys.exit(0) 
signal.signal(signal.SIGINT, signal_handler) 
print 'Press Ctrl+C' 
signal.pause() 

Esempio 2

import signal, os 

def handler(signum, frame): 
    print 'Signal handler called with signal', signum 
    raise IOError("Couldn't open device!") 

# Set the signal handler and a 5-second alarm 
signal.signal(signal.SIGALRM, handler) 
signal.alarm(5) 

# This open() may hang indefinitely 
fd = os.open('/dev/ttyS0', os.O_RDWR) 

signal.alarm(0)   # Disable the alarm 

E almeno qualcosa non funziona su quel link Twitter, vedere here

  • non dimenticate di mettere conn.Close() dopo conn.perform()

Ed è utile avere la modalità di debug abilitata durante il test.

import pycurl 

username = 'your_user_name' 
password = 'your_password' 

def body(buf): 
    for item in buf.strip().split('\n'): 
     if item.strip(): 
      print item 

def test(debug_type, debug_msg): 
    if len(debug_msg) < 300: 
     print "debug(%d): %s" % (debug_type, debug_msg.strip()) 

conn = pycurl.Curl() 
conn.setopt(pycurl.USERNAME, username) 
conn.setopt(pycurl.PASSWORD, password) 
#conn.setopt(pycurl.SSL_VERIFYPEER, False) 
conn.setopt(pycurl.FOLLOWLOCATION, True) 
conn.setopt(pycurl.VERBOSE, True) 
conn.setopt(pycurl.URL, 'https://stream.twitter.com/1.1/statuses/sample.json') 
conn.setopt(pycurl.DEBUGFUNCTION, test) 
conn.setopt(pycurl.WRITEFUNCTION, body) 
conn.perform() 
conn.close() 

Basta copia/incolla di lavoro di test Esempio

➜ ~ hcat twitter.py 
import pycurl 
import signal 
import sys 
from time import sleep 

username = 'bubudee' 
password = 'deebubu' 

def body(buf): 
    for item in buf.strip().split('\n'): 
     if item.strip(): 
      print item 

def test(debug_type, debug_msg): 
    if len(debug_msg) < 300: 
     print "debug(%d): %s" % (debug_type, debug_msg.strip()) 

def handle_ctrl_c(signal, frame): 
    print "Got ctrl+c, going down!" 
    sys.exit(0) 
signal.signal(signal.SIGINT, handle_ctrl_c) 

conn = pycurl.Curl() 
conn.setopt(pycurl.USERNAME, username) 
conn.setopt(pycurl.PASSWORD, password) 
#conn.setopt(pycurl.SSL_VERIFYPEER, False) 
conn.setopt(pycurl.FOLLOWLOCATION, True) 
conn.setopt(pycurl.VERBOSE, True) 
conn.setopt(pycurl.URL, 'https://stream.twitter.com/1.1/statuses/sample.json') 
conn.setopt(pycurl.DEBUGFUNCTION, test) 
conn.setopt(pycurl.WRITEFUNCTION, body) 

conn.perform() 

print "Who let the dogs out?:p" 
sleep(10) 

conn.close() 

➜ ~ python twitter.py 
debug(0): About to connect() to stream.twitter.com port 443 (#0) 
debug(0): Trying 199.16.156.110... 
debug(0): Connected to stream.twitter.com (199.16.156.110) port 443 (#0) 
debug(0): Initializing NSS with certpath: sql:/etc/pki/nssdb 
debug(0): CAfile: /etc/pki/tls/certs/ca-bundle.crt 
    CApath: none 
debug(0): SSL connection using SSL_RSA_WITH_RC4_128_SHA 
debug(0): Server certificate: 
debug(0): subject: CN=stream.twitter.com,OU=Twitter Security,O="Twitter, Inc.",L=San Francisco,ST=California,C=US 
debug(0): start date: Oct 09 00:00:00 2013 GMT 
debug(0): expire date: Dec 30 23:59:59 2016 GMT 
debug(0): common name: stream.twitter.com 
debug(0): issuer: CN=VeriSign Class 3 Secure Server CA - G3,OU=Terms of use at https://www.verisign.com/rpa (c)10,OU=VeriSign Trust Network,O="VeriSign, Inc.",C=US 
debug(0): Server auth using Basic with user 'bubudee' 
debug(2): GET /1.1/statuses/sample.json HTTP/1.1 
Authorization: Basic YnVidWRlZTpkZWVidWJ1 
User-Agent: PycURL/7.29.0 
Host: stream.twitter.com 
Accept: */* 
debug(1): HTTP/1.1 401 Unauthorized 
debug(0): Authentication problem. Ignoring this. 
debug(1): WWW-Authenticate: Basic realm="Firehose" 
debug(1): Content-Type: text/html 
debug(1): Cache-Control: must-revalidate,no-cache,no-store 
debug(1): Content-Length: 1243 
debug(1): Connection: close 
debug(1): 
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 
<title>Error 401 Unauthorized</title> 
</head> 
<body> 
<h2>HTTP ERROR: 401</h2> 
<p>Problem accessing '/1.1/statuses/sample.json'. Reason: 
<pre> Unauthorized</pre> 
</body> 
</html> 
debug(0): Closing connection 0 
Who let the dogs out?:p 
^CGot ctrl+c, going down! 
+1

L'aggiunta di 'close()' non cambierà il fatto che un'eccezione viene sollevata all'interno di 'body()' quando viene interrotta. – Wookai

+0

@Wookai, sì, hai ragione, devi prendere il segnale CTRL + C e processarlo, aggiungendo esempi come. Grazie. –

+0

Potresti mescolare i due in un esempio che funziona con pycurl? – Wookai