|
@@ -221,6 +221,78 @@ The `assert` statement should be used judiciously. Most of the time,
|
|
it is better to catch exceptions, either handle the problem or display
|
|
it is better to catch exceptions, either handle the problem or display
|
|
an error message to the user and then quit.
|
|
an error message to the user and then quit.
|
|
|
|
|
|
|
|
+## Decorators ##
|
|
|
|
+
|
|
|
|
+Decorators are a shortcut to applying wrapper functions. This is
|
|
|
|
+helpful to "wrap" functionality with the same code over and over
|
|
|
|
+again. For example, I created a `retry` decorator for myself that I
|
|
|
|
+can just apply to any function and if any exception is thrown during a
|
|
|
|
+run, it is retried again, till a maximum of 5 times and with a delay
|
|
|
|
+between each retry. This is especially useful for situations where you
|
|
|
|
+are trying to make a network call to a remote computer:
|
|
|
|
+
|
|
|
|
+~~~python
|
|
|
|
+from time import sleep
|
|
|
|
+from functools import wraps
|
|
|
|
+import logging
|
|
|
|
+logging.basicConfig()
|
|
|
|
+log = logging.getLogger("retry")
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def retry(f):
|
|
|
|
+ @wraps(f)
|
|
|
|
+ def wrapped_f(*args, **kwargs):
|
|
|
|
+ MAX_ATTEMPTS = 5
|
|
|
|
+ for attempt in range(1, MAX_ATTEMPTS + 1):
|
|
|
|
+ try:
|
|
|
|
+ return f(*args, **kwargs)
|
|
|
|
+ except:
|
|
|
|
+ log.exception("Attempt %s/%s failed : %s",
|
|
|
|
+ attempt,
|
|
|
|
+ MAX_ATTEMPTS,
|
|
|
|
+ (args, kwargs))
|
|
|
|
+ sleep(10 * attempt)
|
|
|
|
+ log.critical("All %s attempts failed : %s",
|
|
|
|
+ MAX_ATTEMPTS,
|
|
|
|
+ (args, kwargs))
|
|
|
|
+ return wrapped_f
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+counter = 0
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+@retry
|
|
|
|
+def save_to_database(arg):
|
|
|
|
+ print("Write to a database or make a network call or etc.")
|
|
|
|
+ print("This will be automatically retried if exception is thrown.")
|
|
|
|
+ global counter
|
|
|
|
+ counter += 1
|
|
|
|
+ if counter < 2:
|
|
|
|
+ raise ValueError(arg)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+if __name__ == '__main__':
|
|
|
|
+ save_to_database("Some bad value")
|
|
|
|
+~~~
|
|
|
|
+
|
|
|
|
+Output:
|
|
|
|
+
|
|
|
|
+~~~
|
|
|
|
+$ python3 retry_decorator.py
|
|
|
|
+
|
|
|
|
+Write to a database or make a network call or etc.
|
|
|
|
+This will be automatically retried if exception is thrown.
|
|
|
|
+ERROR:retry:Attempt 1/5 failed : (('Some bad value',), {})
|
|
|
|
+Traceback (most recent call last):
|
|
|
|
+ File "/tmp/retry_decorator.py", line 14, in wrapped_f
|
|
|
|
+ return f(*args, **kwargs)
|
|
|
|
+ File "/tmp/retry_decorator.py", line 37, in save_to_database
|
|
|
|
+ raise ValueError(arg)
|
|
|
|
+ValueError: Some bad value
|
|
|
|
+Write to a database or make a network call or etc.
|
|
|
|
+This will be automatically retried if exception is thrown.
|
|
|
|
+~~~
|
|
|
|
+
|
|
## Differences between Python 2 and Python 3 ##
|
|
## Differences between Python 2 and Python 3 ##
|
|
|
|
|
|
Read these articles:
|
|
Read these articles:
|