<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def wait_until(obj, att, desired, callback=None, interval=5, attempts=0, verbose=False, verbose_atts=None):
 """ When changing the state of an object, it will commonly be in a transitional state until the change is complete. This will reload the object every `interval` seconds, and check its `att` attribute until the `desired` value is reached, or until the maximum number of attempts is reached. The updated object is returned. It is up to the calling program to check the returned object to make sure that it successfully reached the desired state. Once the desired value of the attribute is reached, the method returns. If not, it will re-try until the attribute's value matches one of the `desired` values. By default (attempts=0) it will loop infinitely until the attribute reaches the desired value. You can optionally limit the number of times that the object is reloaded by passing a positive value to `attempts`. If the attribute has not reached the desired value by then, the method will exit. If `verbose` is True, each attempt will print out the current value of the watched attribute and the time that has elapsed since the original request. Also, if `verbose_atts` is specified, the values of those attributes will also be output. If `verbose` is False, then `verbose_atts` has no effect. Note that `desired` can be a list of values; if the attribute becomes equal to any of those values, this will succeed. For example, when creating a new cloud server, it will initially have a status of 'BUILD', and you can't work with it until its status is 'ACTIVE'. However, there might be a problem with the build process, and the server will change to a status of 'ERROR'. So for this case you need to set the `desired` parameter to `['ACTIVE', 'ERROR']`. If you simply pass 'ACTIVE' as the desired state, this will loop indefinitely if a build fails, as the server will never reach a status of 'ACTIVE'. Since this process of waiting can take a potentially long time, and will block your program's execution until the desired state of the object is reached, you may specify a callback function. The callback can be any callable that accepts a single parameter; the parameter it receives will be either the updated object (success), or None (failure). If a callback is specified, the program will return immediately after spawning the wait process in a separate thread. """

    if callback:
        waiter = _WaitThread(obj=obj, att=att, desired=desired, callback=callback,
                interval=interval, attempts=attempts, verbose=verbose,
                verbose_atts=verbose_atts)
        waiter.start()
        return waiter
    else:
        return _wait_until(obj=obj, att=att, desired=desired, callback=None,
                interval=interval, attempts=attempts, verbose=verbose,
                verbose_atts=verbose_atts)