2012-05-04 24 views
6

Estoy escribiendo un script en Python usando pycurl consumir Twitter's Sreaming API. He aquí un breve fragmento que hace exactamente eso (en pocas palabras su nombre de usuario de Twitter/contraseña para probarlo):Cómo manejar KeyboardInterrupt (Ctrl-c) muy bien 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() 

El problema es que debido a que el guión consume una corriente, conn.perform() nunca se regresa (o muy raramente). Por lo tanto, a veces necesito interrumpir el script, y el KeyboardInterrupt es capturado por el método perform().

Sin embargo, no lo maneja bien, imprime un error feo y genera una excepción diferente.

^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)') 

El cURL FAQ dice que para interrumpir una transferencia en curso, una de las funciones de devolución de llamada (en mi caso handleData) debe devolver un valor especial. ¡Esto es genial, pero el KeyboardInterrupt no es capturado por ninguna de la función de devolución de llamada!

¿Cómo puedo hacer esto perfectamente?

EDIT: Sé que usted puede capturar las excepciones, pero pycurl sigue algunas cosas divertidas:

Si hago:

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

me sale:

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

Esto significa que internamente, pycurl realiza algún tipo de captura, imprime un feo mensaje de error y luego levanta un pycurl.error.

+0

'KeyboardInterrupt' [no es una subclase de' Exception', que es una subclase de 'BaseException'] (http://docs.python.org/library/exceptions.html#exception-hierarchy); sin embargo, eso no cambia el resultado. (Aunque es otro ejemplo de por qué atrapar 'Exception' es una mala idea.) –

+0

Gracias por señalarlo, corregí mi ejemplo. Sin embargo, no cambia mi pregunta. – Wookai

+0

Necesita conn.close() después de conn.perform(). –

Respuesta

1

Puede hacerlo capturando el tipo pycurl.error. Ej:

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

Esto es lo mismo que la segunda cosa que probé, que no elimina la impresión KeyboardInterrupt. – Wookai

2

usted necesita coger CTRL + C y procesar esa señal
original: Example 1
original: Example 2


Ejemplo 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() 

Ejemplo 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 

Y por lo menos algo no está funcionando en ese enlace gorjeo, ver here

  • no se olvide de poner conn.Close() después deconn.perform()

Y es útil tener habilitado el modo de depuración cuando se prueba.

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() 

Sólo copiar/pegar de trabajo Ejemplo de ensayo

➜ ~ 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

Agregar 'close()' no cambiará el hecho de que se genera una excepción dentro de 'body()' cuando se interrumpe. – Wookai

+0

@Wookai, sí, tiene razón, necesita capturar la señal CTRL + C y procesarla, agregó ejemplos de cómo hacerlo. Gracias. –

+0

¿Podría mezclar los dos en un ejemplo que funciona con pycurl? – Wookai