Python dynamic exception capture

Source: Internet
Author: User
Tags socket error
What surprised me when discussing how to dynamically capture exceptions is that it can help me find hidden bugs and fun... problematic code the following code comes from an abstract code that looks good in a product-sl... what surprised me when discussing how to dynamically capture exceptions is that it can help me find hidden bugs and fun...

Problematic code


The following code comes from an abstract code that looks good in a product-slightly (!) . This is to call some statistical data functions and then process them. first, use the socket connection to obtain a value. a socket Error may occur. because the statistical data is not crucial in the system, we just remember the log errors and continue.

(Please note that this article I tested using doctest-this indicates that the code can run !)

>>> Def get_stats ():

... Pass

...

>>> Def do_something_with_stats (stats ):

... Pass

...

>>> Try:

... Stats = get_stats ()

... Couldn't socket. error:

... Logging. warning ("Can't get statistics ")

... Else:

... Do_something_with_stats (stats)

Search


We did not find any problem during the test, but we actually noticed that the static analysis report showed a problem:

$ Flake8 filename. py

Filename. py: 351: 1: F821 undefined name 'socket'

Filename. py: 352: 1: F821 undefined name 'logging'

Obviously, we didn't test it. The problem is that we didn't reference the socket and logging modules in the code. I was surprised that no NameError error was thrown in advance. I thought it would look for some nouns in these exception statements, such as the exceptions to be captured, what does it need to know!

It turns out that this is not the case. the query of an exception statement is delayed, but an exception is thrown during evaluation. you can also customize the display declaration exception as 'parameter (argument )'.

This may be a good thing, a bad thing, or an unpleasant thing.

Good deeds (as mentioned in the previous section)

The exception parameter can be passed in any form of numerical value, so that the dynamic parameters of the exception can be captured.

>>> Def do_something ():

... Blob

...

>>> Def attempt (action, ignore_spec ):

... Try:

... Action ()

... Ignore T ignore_spec:

... Pass

...

>>> Attempt (do_something, ignore_spec = (NameError, TypeError ))

>>> Attempt (do_something, ignore_spec = TypeError)

Traceback (most recent call last ):

...

NameError: global name 'blob 'is not defined

Bad (as mentioned in the previous section)

This obvious drawback is that errors in the exception parameters are usually noticed only after the exception is triggered, but it is too late. when exceptions are used to capture uncommon events (for example, opening a file in write mode fails), unless a specific test case is used, only one exception (or any exception) is allowed) it is only known when it is triggered. at that time, it is recorded to check whether there is a matching exception, and its own error exception is thrown-this is a typical NameError.

>>> Def do_something ():

... Return 1, 2

...

>>> Try:

... A, B = do_something ()

... Couldn't ValuError: # oops-someone can't type

... Print ("Oops ")

... Else:

... Print ("OK! ") # We are 'OK' until do_something returns a triple...

OK!

Annoying (as mentioned in the previous section)

>>> Try:

... TypeError = ZeroDivisionError # now why wocould we do this ...?!

.... 1/0

... Handle T TypeError:

... Print ("Caught! ")

... Else:

... Print ("OK ")

...

Caught!

Not only does the exception parameter work by name, but other expressions also work like this:

>>> Try:

.... 1/0

... Lost T eval (''. join ('zero Division error'. split ())):

... Print ("Caught! ")

... Else:

... Print ("OK ")

...

Caught!

The exception parameter is not only determined at runtime, but can even use the exception information during the lifecycle. the following is a rather confusing method to capture thrown exceptions-but this is also the case:

>>> Import sys

>>> Def current_exc_type ():

... Return sys. exc_info () [0]

...

>>> Try:

... Blob

... Wait T current_exc_type ():

... Print ("Got you! ")

...

Got you!

Obviously, this is what we really want to find. when we write an exception handler, we should first think of this

(Byte) code

To confirm how it appears in The Exception handling process, I run dis in an exception example. dis (). (Note that the decomposition here is produced under Python2.7-different bytecode is generated under Python 3.3, but this is basically similar ):

>>> Import dis

>>> Def x ():

... Try:

... Pass

... Blocks T Blobbity:

... Print ("bad ")

... Else:

... Print ("good ")

...

>>> Dis. dis (x) # doctest: + NORMALIZE_WHITESPACE

2 0 setup_0000t 4 (to 7)

3 3 POP_BLOCK

4 JUMP_FORWARD 22 (to 29)

4> 7 DUP_TOP

8 LOAD_GLOBAL 0 (Blobbity)

11 COMPARE_OP 10 (exception match)

14 POP_JUMP_IF_FALSE 28

17 POP_TOP

18 POP_TOP

19 POP_TOP

5 20 LOAD_CONST 1 ('bad ')

23 PRINT_ITEM

24 PRINT_NEWLINE

25 JUMP_FORWARD 6 (to 34)

> 28 END_FINALLY

7> 29 LOAD_CONST 2 ('good ')

32 PRINT_ITEM

33 PRINT_NEWLINE

> 34 LOAD_CONST 0 (None)

37 RETURN_VALUE

This shows the problem I expected (issue ). exception handling "seems" to be completely running according to the Python internal mechanism. in this step, there is no need to know the "capture" statements about subsequent exceptions, and they will be completely ignored if no exceptions are thrown. setup_0000t does not care about what happened. it is just that if an exception occurs, the first handler should be evaluated, the second handler, and so on.

Each handler consists of two parts: the rule for obtaining an exception, which is compared with the exception just thrown. everything is delayed, and everything seems to be as expected for your line-by-line code, from the interpreter's perspective. no task is clever, but suddenly it looks very intelligent.

Summary

Although this dynamic exception parameter surprised me, it contains many interesting applications. of course, implementing many of them may be a bad idea.

Sometimes it is not always intuitive to determine how many Python features are supported-for example, expressions and declarations in the class scope are explicitly accepted (rather than functions, methods, and global scopes ), but not all of them are so flexible. although (I think) that would be wonderful, expressions are not allowed to be applied to the decorator-The following is a Python syntax error:

@ (Lambda fn: fn)

Def x ():

Pass

This is an example of trying to pass a dynamic exception parameter to the first exception through a given type, and quietly endure repeated exceptions:

>>> Class Pushover (object ):

... Exc_spec = set ()

...

... Def attempt (self, action ):

... Try:

... Return action ()

... Counter t tuple (self. exc_spec ):

... Pass

... Encode T BaseException as e:

... Self. exc_spec.add (e. _ class __)

... Raise

...

>>> Pushover = Pushover ()

>>>

>>> For _ in range (4 ):

... Try:

... Pushover. attempt (lambda: 1/0)

... Handle t:

... Print ("Boo ")

... Else:

... Print ("Yay! ")

Boo

Yay!

Yay!

Yay!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.