Example #1
0
    def test_at_queue_validity(self):
        valid = atq._validate_queue

        self.assertRaises(ValueError, atq._validate_queue, 'aa')
        self.assertRaises(ValueError, atq._validate_queue, '1')
        self.assertRaises(ValueError, atq._validate_queue, u'\u2022')

        # This one is a trick question ... "=" is technically a valid queue,
        # but only in GNU at. It stands for the currently running queue, which
        # means that in either version it should not be allowed to assign jobs
        # to it.
        self.assertRaises(ValueError, atq._validate_queue, '=')

        self.assertEqual('Q', atq._validate_queue('Q'))
Example #2
0
def at(command, when, queue='a'):
    """ Execute command at when.

        command may be anything interpreble by /bin/sh. If you need features
        specific to another shell, create a script and then make the command
        <path to shell> <path to script>.

        when may be a datetime.timedelta, a datetime.datetime or a timespec str.
        If a string is provided, it is assumed that it's a valid timespec. See
        `timespec` doc in `at`'s documentation.

        python-atd also has good support for named queues. Both GNU and BSD at
        support the concept of named queues, which allow you to easily separate
        different types of jobs based on type. For example, if you owned a bank,
        you'd have different types of jobs. Check clearing might go in queue "c"
        24 hours after request, while international wire clearing would go in
        queue "i" 48 hours after request. An unfortunate limitation of `at` is
        that all jobs can only be one letter, A-Z or a-z. This means there are
        only 52 available queues in both BSD at and GNU at. """

    # First build our timespec for `at`...
    posix_time = False
    # Since timespecs aren't validated/parsed, we can only easily check datetime
    check_past = False
    if isinstance(when, datetime.datetime):
        timespec = convert_datetime(when)
        posix_time = True
        check_past = True
    elif isinstance(when, datetime.timedelta):
        timespec = convert_timedelta(when)
        when = datetime.datetime.now()+when
        check_past = True
    elif isinstance(when, str):
        timespec = when  # TODO: Validate timespec?
    else:
        raise NotImplementedError('I don\'t support the class you passed to schedule(). Try the builtin datetime.')

    if check_past:
        if (when < datetime.datetime.now()):
            raise ValueError('`when` must be at a time in the future, never in the past')

    # Build our `at` command line arguments...
    atargs = list([config.at_binary])
    queue = _validate_queue(queue)
    if posix_time:
        atargs.append('-t')

    atargs.extend(timespec.split(" "))

    if config.always_send_mail:
        atargs.append('-m')
    elif config.never_send_mail:
        atargs.append('-M')

    atargs.append('-q')
    atargs.append(queue)

    # Prevent creation of a needless subprocess by using a temporary file.
    # StringIO cannot be used due to Popen's use of fileno().
    at_stdin = tempfile.TemporaryFile('w')
    at_stdin.write(command)
    at_stdin.seek(0)

    # Build our Popen keyword arguments...
    atkwargs = dict(stdin=at_stdin, stdout=PIPE, stderr=PIPE)
    if not config.inherit_env:
        atkwargs['env'] = config.atjob_environment

    sp = Popen(atargs, **atkwargs)
    (at_stdout, at_stderr) = sp.communicate()
    at_stdin.close()

    # Build our AtJob object for user consumption...
    atjob = AtJob()
    atjob.from_at_stderr(at_stderr)
    atjob.command = command
    atjob.when = when
    atjob.queue = queue
    atjob.who = os.getenv("LOGNAME")

    return atjob