Sleep() in Python

Introduction

Have you ever had to wait for something in a Python program? You typically want your code to run as rapidly as possible. But there are instances when it actually serves your best interests to put your code to sleep for a bit.

For instance, to mimic a delay in your program, you may use the sleep() method in Python. It may be necessary for you to wait while a file uploads or downloads, a graphic loads or is drawn to the screen. Even between calls to a web API or database queries, you might need to take a break. Each of these situations, as well as many more, can benefit from adding Python sleep() calls to your application.

In this lesson, we'll learn how to add sleep() calls to Python using:

  • time.sleep()
  • Decorators
  • Threads

Including a sleep() call with time in Python.

Python includes built-in functionality to put your programme to sleep, called sleep(). The sleep() function in the time module allows you to pause the caller thread's execution for however long you choose.

Here's an illustration of how to utilize time.sleep():

>>> import time
>>> time.sleep(3) # Sleep for 3 seconds

You should wait before entering a new statement into the REPL if you execute this code in your console.

$ python3 -m timeit -n 3 "import time; time.sleep(3)"
3 loops, best of 5: 3 sec per loop

Python's timeit module can be used to gauge how long a sleep session lasts:

Here, the -n argument is used to instruct the timeit module how many times to execute the next sentence. You can see that timeit executed the statement three times, with the best run time coming in at 3 seconds, as was predicted.

One million times is the default quantity of times that timeit will execute your code. The preceding code would hang your terminal for about 34 days if you ran it with the default -n and at a 3 second iteration rate. You can review the documentation for the timeit module to learn about a number of additional command-line parameters.

Let's make something a little more plausible. An outage on one of their websites must be reported to the system administrator. You need to be able to regularly monitor the status code of the website, but you can't continually query the web server without degrading performance. Using the Python sleep() system function is one method of performing this check:

import time
import urllib.request
import urllib.error

def uptime_bot(url):
    while True:
        try:
            conn = urllib.request.urlopen(url)
        except urllib.error.HTTPError as e:
            # Email admin / log
            print(f'HTTPError: {e.code} for {url}')
        except urllib.error.URLError as e:
            # Email admin / log
            print(f'URLError: {e.code} for {url}')
        else:
            # Website is up
            print(f'{url} is up')
        time.sleep(60)

if __name__ == '__main__':
    url = 'http://www.google.com/py'
    uptime_bot(url)

write your code here: Coding Playground

Uptime_ bot(), which accepts a URL as an argument, is created here. The function then tries using urllib to open that URL. The application detects errors such as HTTPErrors and prints them out if they occur. (If the error occurred in a live environment, you would log it and possibly send the webmaster or system administrator an email.)

Your code prints out everything is ok if there are no mistakes. Your programme will sleep for 60 seconds no matter what occurs. This indicates that you don't visit the website more than once every minute. The URL used in this example is flawed, therefore it will continuously output the following information to your console:

HTTPError: 404 for http://www.google.com/py

Including a sleep() call with decorators in Python

You occasionally need to try a failed function again. When you have to retry a file download because the server was overloaded, this is a frequent use scenario. Adding a Python sleep() call in between each request is preferable because you generally won't want to send requests to the server frequently.

I've also had to monitor the status of a user interface while running an automated test, which is another use case. Depending on the machine I'm using to do the test, the user interface may load quicker or slower than usual. This could alter the information displayed on the screen when my software is checking anything.

In this situation, I can instruct the application to take a little break and then verify everything again a moment or two later. This may determine whether a test is passed or failed.

In either of these scenarios, you may add a Python sleep() system call using a decorator. Check see Primer on Python Decorators if you want to brush up on decorators or if you're not familiar with them. Let's examine an illustration:

import time
import urllib.request
import urllib.error

def sleep(timeout, retry=3):
    def the_real_decorator(function):
        def wrapper(*args, **kwargs):
            retries = 0
            while retries < retry:
                try:
                    value = function(*args, **kwargs)
                    if value is None:
                        return
                except:
                    print(f'Sleeping for {timeout} seconds')
                    time.sleep(timeout)
                    retries += 1
        return wrapper
    return the_real_decorator

write your code here: Coding Playground

This is your decorator: sleep(). It receives a timeout value and a retry count, with a default value of three. The real decorator(), a function that accepts the decorated function, is contained within the sleep() function.

Last but not least, the innermost function wrapper() takes both keyword arguments and parameters passed to the decorated function. The magic happens right here! You try invoking the procedure once more by using a while loop. If there is an exception, you should declare it over. Try running the function again after using sleep() and increasing the number of retries.

To use your new decorator, edit uptime bot() as follows:

@sleep(3)
def uptime_bot(url):
    try:
        conn = urllib.request.urlopen(url)
    except urllib.error.HTTPError as e:
        # Email admin / log
        print(f'HTTPError: {e.code} for {url}')
        # Re-raise the exception for the decorator
        raise urllib.error.HTTPError
    except urllib.error.URLError as e:
        # Email admin / log
        print(f'URLError: {e.code} for {url}')
        # Re-raise the exception for the decorator
        raise urllib.error.URLError
    else:
        # Website is up
        print(f'{url} is up')

if __name__ == '__main__':
    url = 'http://www.google.com/py'
    uptime_bot(url)

write your code here: Coding Playground

Here, you add a 3 second sleep() decoration to the uptime bot() function. Additionally, the initial while loop and the previous call to sleep have been eliminated (60). Now, the decorator handles this.

You also added a raise inside the exception handling blocks, which is another change. This will ensure effective operation of the decorator. Although these exceptions only apply to urllib, you might be better off leaving the decorator in its current state. You might write the decorator to handle these problems. It will function with a greater range of functions in this fashion.

Using Threads to Add a Python sleep() Call

There are further instances in which you would want to provide a thread a Python sleep() function. Maybe you're using a production database with millions of records and a migration script. You choose to use threads because you don't want to cause any downtime but you also don't want to wait any longer than required for the migration to be finished.

Each thread must run for a brief period and then sleep in order to avoid clients from perceiving any form of slowness. Two strategies exist for doing this:

1)Utilize time. like previously, use sleep().

2) Use the threading module's Event.wait() method.

Let's begin by examining time.sleep().

Utilizing time. sleep()

The Python Logging Cookbook provides an excellent time-based example.

sleep(). For this exercise, the logging module in Python is a little more advantageous than print() statements because it is thread-safe. Based on this illustration is the code that follows:

import logging
import threading
import time

def worker(arg):
    while not arg["stop"]:
        logging.debug("worker thread checking in")
        time.sleep(1)

def main():
    logging.basicConfig(
        level=logging.DEBUG,
        format="%(relativeCreated)6d %(threadName)s %(message)s"
    )
    info = {"stop": False}
    thread = threading.Thread(target=worker, args=(info,))
    thread_two = threading.Thread(target=worker, args=(info,))
    thread.start()
    thread_two.start()

    while True:
        try:
            logging.debug("Checking in from main thread")
            time.sleep(0.75)
        except KeyboardInterrupt:
            info["stop"] = True
            logging.debug('Stopping')
            break
    thread.join()
    thread_two.join()

if __name__ == "__main__":
    main()

write your code here: Coding Playground

The use of Event.wait ()

You can use Event() from the threading module in place of time.sleep (). The additional advantage of Event() is that it is more responsive. This is because the application will instantly exit the loop after the event is set. Your code will need to wait for the Python sleep() function to complete before the thread can terminate if you use time.sleep().

Because wait() is non-blocking and time.sleep() is blocking, you should use wait() in this situation. This means that when you use time.sleep(), the main thread will be blocked from operating while it waits for the sleep() call to complete. The solution is wait(). The threading manual for Python contains more information on how this all functions.

import logging
import threading
import time

def worker(arg):
    while not arg["stop"]:
        logging.debug("worker thread checking in")
        time.sleep(1)

def main():
    logging.basicConfig(
        level=logging.DEBUG,
        format="%(relativeCreated)6d %(threadName)s %(message)s"
    )
    info = {"stop": False}
    thread = threading.Thread(target=worker, args=(info,))
    thread_two = threading.Thread(target=worker, args=(info,))
    thread.start()
    thread_two.start()

    while True:
        try:
            logging.debug("Checking in from main thread")
            time.sleep(0.75)
        except KeyboardInterrupt:
            info["stop"] = True
            logging.debug('Stopping')
            break
    thread.join()
    thread_two.join()

if __name__ == "__main__":
    main()

write your code here: Coding Playground