'python setting state of a future

Is it bad practice to set the state of future to pass arguments? Specifically using something like future.q = q to use q in the callback

from threading import Thread
from threading import RLock
from threading import current_thread
from concurrent.futures import Future
import time
import random

class NonBlockingQueue:

    def __init__(self, max_size):
        self.max_size = max_size
        self.q = []
        self.q_waiting_puts = []
        self.q_waiting_gets = []
        self.lock = RLock()

    def enqueue(self, item):

            future = None
            with self.lock:
                curr_size = len(self.q)

                # queue is full so create a future for a put
                # request
                if curr_size == self.max_size:
                    future = Future()
                    self.q_waiting_puts.append(future)

                else:
                    self.q.append(item)

                    # remember to resolve a pending future for
                    # a get request
                    if len(self.q_waiting_gets) != 0:
                        future_get = self.q_waiting_gets.pop(0)
                        future_get.set_result(self.q.pop(0))

            return future


    def retry_enqueue(future):
        print("\nCallback invoked by thread {0}".format(current_thread().getName()))
        item = future.item
        q = future.q
        new_future = q.enqueue(item)

        if new_future is not None:
            new_future.item = item
            new_future.q = q
            new_future.add_done_callback(retry_enqueue)
        else:
            print("\n{0} successfully added on a retry".format(item))



### MAIN CODE
def producer_thread(q):
    item = 1
    while 1:
        future = q.enqueue(item)
        if future is not None:
            future.item = item
            future.q = q
            future.add_done_callback(retry_enqueue)

        item += 1

        # slow down the producer
        time.sleep(random.randint(1, 3))


Solution 1:[1]

It is not a good idea to pass around arguments like this.

The reason is that in future (no pun), they could just disallow setting custom attributes on the Future object, which will break your code.

Better solution is to use functools.partial or lambda to pass extra arguments to the callback.

First, accept q as an argument in the retry_enqueue function:


def retry_enqueue(future, q): # accept 'q' argument
    ...

Example using functools.partial:

import functools

future.add_done_callback(functools.partial(retry_enqueue, q=q))

Example using lambda:

future.add_done_callback(lambda future: retry_enqueue(future, q))

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 xyres