2011-01-05 61 views
32

Tôi đang viết python 2.6.6 mã trên cửa sổ trông như thế này:Tại sao tôi không thể xử lý KeyboardInterrupt trong python?

try: 
    dostuff() 
except KeyboardInterrupt: 
    print "Interrupted!" 
except: 
    print "Some other exception?" 
finally: 
    print "cleaning up...." 
    print "done." 

dostuff() là một chức năng mà vòng mãi mãi, đọc một dòng cùng một lúc từ một dòng đầu vào và tác động lên nó. Tôi muốn để có thể ngăn chặn nó và làm sạch khi tôi nhấn ctrl-c.

Điều đang xảy ra thay vào đó là mã dưới except KeyboardInterrupt: không hoạt động chút nào. Điều duy nhất mà được in là "dọn dẹp ...", và sau đó một traceback được in trông như thế này:

Traceback (most recent call last): 
    File "filename.py", line 119, in <module> 
    print 'cleaning up...' 
KeyboardInterrupt 

Vì vậy, mã xử lý ngoại lệ là không được chạy, và traceback tuyên bố rằng một KeyboardInterrupt xảy ra trong mệnh đề cuối cùng, điều này không có ý nghĩa bởi vì nhấn ctrl-c là nguyên nhân khiến phần đó chạy ở vị trí đầu tiên! Ngay cả các khoản except: chung không chạy.

EDIT: Dựa trên nhận xét, tôi đã thay thế nội dung của khối try: bằng sys.stdin.read(). Vấn đề vẫn xảy ra chính xác như được mô tả, với dòng đầu tiên của khối finally: đang chạy và sau đó in cùng một dấu vết.

EDIT # 2: Nếu tôi thêm nhiều thứ sau khi đọc, trình xử lý hoạt động. Vì vậy, điều này không:

try: 
    sys.stdin.read() 
except KeyboardInterrupt: 
    ... 

Nhưng công trình này:

try: 
    sys.stdin.read() 
    print "Done reading." 
except KeyboardInterrupt: 
    ... 

Dưới đây là những gì đang in là: "Xong đọc"

Done reading. Interrupted! 
cleaning up... 
done. 

Vì vậy, đối với một số lý do, dòng được in, mặc dù ngoại lệ xảy ra trên dòng trước đó. Đó không thực sự là một vấn đề - rõ ràng là tôi phải có khả năng xử lý một ngoại lệ ở bất cứ đâu trong khối "thử". Tuy nhiên, các bản in không hoạt động bình thường - nó không in một dòng mới sau đó như nó được cho là! Các "Interruped" được in trên cùng một dòng ... với một không gian trước đó, vì một lý do nào đó ...? Dù sao đi nữa, sau đó mã đó thực hiện những gì nó được yêu cầu.

Dường như với tôi rằng đây là lỗi trong việc xử lý gián đoạn trong suốt cuộc gọi hệ thống bị chặn.

+5

Hiển thị mã cho dostuff() của bạn, vì mã này sẽ hoạt động (và nó) – user225312

+0

Nó hoạt động như mong đợi với Python 2.5.1. – khachik

+4

sao chép bằng Python 2.7, thay thế 'dostuff()' bằng 'sys.stdin.read()' – balpha

Trả lời

0

Dưới đây là một dự đoán về những gì đang xảy ra: (? Vì lý do gì ... lỗi Win32 giới hạn)

  • nhấn Ctrl-C phá vỡ "in" tuyên bố
  • nhấn Ctrl-C cũng ném Đầu tiên KeyboardInterrupt, trong dostuff()
  • Trình xử lý ngoại lệ chạy và cố gắng in "Bị gián đoạn", nhưng câu lệnh "print" bị hỏng và ném một KeyboardInterrupt khác.
  • Điều khoản cuối cùng chạy và cố gắng in "làm sạch ....", nhưng câu lệnh "in" bị hỏng và ném thêm một KeyboardInterrupt khác.
+0

Bạn có thể tái tạo sự cố không? Windows tôi giả sử? – user225312

+1

Không có cách nào ngoại lệ có thể "phá vỡ" 'in' (hoặc bất kỳ chức năng nào khác cho vấn đề đó). Ngoài ra điều này sẽ nhận được một n-lần-xếp chồng lên nhau với "trong khi xử lý các trường hợp ngoại lệ trên, một ngoại lệ đã xảy ra" ở giữa. – delnan

18

Xử lý ngoại lệ không đồng bộ không may là không đáng tin cậy (ngoại lệ do trình xử lý tín hiệu đưa ra, ngữ cảnh bên ngoài thông qua C API, v.v.).Bạn có thể tăng cơ hội xử lý ngoại lệ async đúng cách nếu có một số điều phối trong mã về đoạn mã nào chịu trách nhiệm bắt chúng (cao nhất có thể trong ngăn xếp cuộc gọi có vẻ thích hợp ngoại trừ các hàm rất quan trọng).

Chức năng được gọi (dostuff) hoặc các chức năng tiếp tục xuống ngăn xếp có thể tự nó có lỗi cho KeyboardInterrupt hoặc BaseException mà bạn không/không thể giải thích.

trường hợp tầm thường này chỉ làm việc tốt với python 2.6.6 (x64) tương tác + Windows 7 (64bit):

>>> import time 
>>> def foo(): 
...  try: 
...    time.sleep(100) 
...  except KeyboardInterrupt: 
...    print "INTERRUPTED!" 
... 
>>> foo() 
INTERRUPTED! #after pressing ctrl+c 

EDIT:

Sau khi điều tra thêm, tôi đã cố gắng những gì tôi tin là ví dụ mà những người khác đã sử dụng để tái tạo vấn đề. Tôi đã lười vì vậy tôi đã bỏ qua "cuối cùng"

>>> def foo(): 
...  try: 
...    sys.stdin.read() 
...  except KeyboardInterrupt: 
...    print "BLAH" 
... 
>>> foo() 

Trả lại ngay sau khi nhấn CTRL + C. Điều thú vị đã xảy ra khi tôi ngay lập tức cố gắng gọi lại foo:

>>> foo() 

Traceback (most recent call last): 
    File "c:\Python26\lib\encodings\cp437.py", line 14, in decode 
    def decode(self,input,errors='strict'): 
KeyboardInterrupt 

Ngoại lệ được đưa ra ngay lập tức mà không cần nhấn CTRL + C.

Điều này có vẻ hợp lý - có vẻ như chúng tôi đang xử lý các sắc thái trong cách ngoại lệ không đồng bộ được xử lý bằng Python. Có thể thực hiện một số lệnh bytecode trước khi ngoại lệ async thực sự xuất hiện và sau đó được nâng lên trong ngữ cảnh thực thi hiện tại. (Đó là hành vi mà tôi đã nhìn thấy khi chơi với nó trong quá khứ)

Xem API C: http://docs.python.org/c-api/init.html#PyThreadState_SetAsyncExc

Vì vậy, điều này phần nào giải thích tại sao KeyboardInterrupt được nêu ra trong bối cảnh thực hiện các cuối cùng tuyên bố trong ví dụ này:

>>> def foo(): 
...  try: 
...    sys.stdin.read() 
...  except KeyboardInterrupt: 
...    print "interrupt" 
...  finally: 
...    print "FINALLY" 
... 
>>> foo() 
FINALLY 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 7, in foo 
KeyboardInterrupt 

có thể có một số pha trộn điên rồ của bộ xử lý tín hiệu tùy chỉnh trộn với handler KeyboardInterrupt/CTRL + C tiêu chuẩn của thông dịch viên đó là kết quả là loại hành vi này. Ví dụ, cuộc gọi read() nhìn thấy tín hiệu và bails, nhưng nó lại làm tăng tín hiệu sau khi hủy đăng ký nó xử lý. Tôi sẽ không biết chắc chắn nếu không kiểm tra codebase phiên dịch.

Đây là lý do tại sao tôi thường né tránh việc sử dụng các ngoại lệ async ....

EDIT 2

Tôi nghĩ rằng có một trường hợp tốt cho một báo cáo lỗi.

Again hơn lý thuyết ... (chỉ dựa trên mã đọc) Xem nguồn đối tượng file: http://svn.python.org/view/python/branches/release26-maint/Objects/fileobject.c?revision=81277&view=markup

file_read gọi Py_UniversalNewlineFread(). fread có thể trở lại với một lỗi với errno = EINTR (nó thực hiện xử lý tín hiệu riêng của nó). Trong trường hợp này Py_UniversalNewlineFread() bails nhưng không thực hiện bất kỳ kiểm tra tín hiệu với PyErr_CheckSignals() để các trình xử lý có thể được gọi đồng bộ. file_read xóa lỗi tệp nhưng cũng không gọi PyErr_CheckSignals().

Xem getline() và getline_via_fgets() để biết ví dụ về cách nó được sử dụng. Mẫu được ghi lại trong báo cáo lỗi này cho một vấn đề tương tự: (http://bugs.python.org/issue1195).Vì vậy, có vẻ như tín hiệu được xử lý tại một thời điểm không xác định bởi người phiên dịch.

Tôi đoán có rất ít giá trị trong việc lặn sâu hơn vì nó vẫn chưa rõ liệu ví dụ sys.stdin.read() có tương tự đúng với hàm "dostuff()" của bạn hay không. (Có thể có nhiều lỗi tại vở kịch)

+0

Nhưng những người khác (bao gồm cả tôi - Windows 7 32 bit với Python3 ở đây) đã sao chép nó với các thay thế 'dostuff' không bắt KeyboardInterrupt. – delnan

+0

@delnan - câu trả lời được mở rộng. hy vọng điều này sẽ giúp xây dựng một số lý thuyết! :) –

+4

Tôi sẽ không gọi đây là "sắc thái" trong trường hợp ngoại lệ không đồng bộ nhiều như lỗi hoàn toàn. Nếu ngoại lệ được nâng lên muộn hơn một chút so với khi nó xảy ra về mặt kỹ thuật, thì tốt ... nhưng nếu ngoại lệ được nâng lên bên ngoài khối try thì ngoại lệ đó đã thoát ra, đó là một câu chuyện khác. Một số hành vi được làm tài liệu rõ ràng bị hỏng ở đây, quan trọng nhất là khối "cuối cùng" không chạy mã "dọn dẹp" được bảo đảm; và cũng không có trình xử lý ngoại lệ nào được chạy khi có ít nhất một ngoại lệ xảy ra trong khối "thử". – Josh

0

này làm việc cho tôi:

import sys 

if __name__ == "__main__": 
    try: 
     sys.stdin.read() 
     print "Here" 
    except KeyboardInterrupt: 
     print "Worked" 
    except: 
     print "Something else" 
    finally: 
     print "Finally" 

Hãy thử đặt một đường dây bên ngoài của() chức năng doStuff của bạn hoặc di chuyển với điều kiện vòng lặp bên ngoài của hàm. Ví dụ:

try: 
    while True: 
     dostuff() 
except KeyboardInterrupt: 
    print "Interrupted!" 
except: 
    print "Some other exception?" 
finally: 
    print "cleaning up...." 
    print "done." 
1

là cuộc gọi hệ thống và do đó hành vi sẽ khác nhau đối với từng hệ thống. Đối với windows 7 Tôi nghĩ rằng những gì đang xảy ra là đầu vào đang được buffered và vì vậy bạn đang nhận được nơi sys.stdin.read() đang trả về mọi thứ cho đến Ctrl-C và ngay sau khi bạn truy cập sys.stdin một lần nữa nó sẽ gửi "Ctrl- C ".

thử những điều sau đây,

def foo(): 
    try: 
     print sys.stdin.read() 
     print sys.stdin.closed 
    except KeyboardInterrupt: 
     print "Interrupted!" 

Điều này cho thấy rằng có một đệm của stdin xảy ra đó gây ra một cuộc gọi khác đến stdin để nhận các đầu vào bàn phím

def foo(): 
    try: 
     x=0 
     while 1: 
      x += 1 
     print x 
    except KeyboardInterrupt: 
     print "Interrupted!" 

có vẻ không là một vấn đề.

Có phải là dostuff() đọc từ stdin không?

+0

Tôi nghĩ bạn đang làm điều gì đó, ngoại trừ việc bạn thay thế sys.stdin.closed bằng bất kỳ lệnh nào khác, bao gồm in hoặc ngủ, điều đó cũng khiến nó hoạt động. Tôi không nghĩ rằng truy cập stdin một lần thứ hai là cần thiết ... Tôi nghĩ rằng có thể nhấn ctrl-c làm cho hệ thống gọi trở lại và sau đó C sendsNG gửi một gián đoạn cho phần mềm, nhưng python không xử lý ngắt cho đến sau khi dòng tiếp theo của mã ... sau đó nó đã được ra khỏi khối thử! – Josh

+0

Phải, nhưng tôi không chắc chắn những gì bạn đang làm trong 'dostuff()' của bạn cũng sẽ không gây ra điều này xảy ra. – milkypostman

1

Có vấn đề tương tự và đây là cách giải quyết của tôi:

try: 
    some_blocking_io_here() # CTRL-C to interrupt 
except: 
    try: 
     print() # any i/o will get the second KeyboardInterrupt here? 
    except: 
     real_handler_here() 
0
def foo(): 
    try: 
     x=0 
     while 1: 
      x+=1 
      print (x) 
    except KeyboardInterrupt: 
     print ("interrupted!!") 
foo() 

đó làm việc tốt.