~ubuntu-branches/debian/squeeze/aptdaemon/squeeze

« back to all changes in this revision

Viewing changes to aptdaemon/defer.py

  • Committer: Bazaar Package Importer
  • Author(s): Julian Andres Klode
  • Date: 2010-06-06 14:30:27 UTC
  • mfrom: (1.1.18 upstream) (18.1.12 maverick)
  • Revision ID: james.westby@ubuntu.com-20100606143027-tyttr56a1y7lk2h6
Tags: 0.31+bzr413-1
* Merge with Ubuntu, remaining differences:
  - debian/copyright uses DEP-5 format.
  - debian/source/format: Set to "3.0 (quilt)".
  - debian/rules: Use debhelper 7 instead of quilt
  - debian/watch: Added watch file.
  - debian/control: Reindent, Vcs, Maintainer changes.
* debian/patches/03_auth_me_less.patch: Change patch level to 1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/env python
2
2
# -*- coding: utf-8 -*-
3
3
"""Simplified Twisted Deferreds."""
4
 
# Copyright (C) 2008-2009 Sebastian Heinlein <devel@glatzor.de>
5
 
#
6
 
# This program is free software; you can redistribute it and/or modify
7
 
# it under the terms of the GNU General Public License as published by
8
 
# the Free Software Foundation; either version 2 of the License, or
9
 
# any later version.
10
 
#
11
 
# This program is distributed in the hope that it will be useful,
12
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 
# GNU General Public License for more details.
15
 
#
16
 
# You should have received a copy of the GNU General Public License along
17
 
# with this program; if not, write to the Free Software Foundation, Inc.,
18
 
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
4
# Copyright (C) 2008-2010 Sebastian Heinlein <devel@glatzor.de>
 
5
# Copyright (c) 2001-2010
 
6
# Allen Short
 
7
# Andy Gayton
 
8
# Andrew Bennetts
 
9
# Antoine Pitrou
 
10
# Apple Computer, Inc.
 
11
# Benjamin Bruheim
 
12
# Bob Ippolito
 
13
# Canonical Limited
 
14
# Christopher Armstrong
 
15
# David Reid
 
16
# Donovan Preston
 
17
# Eric Mangold
 
18
# Eyal Lotem
 
19
# Itamar Shtull-Trauring
 
20
# James Knight
 
21
# Jason A. Mobarak
 
22
# Jean-Paul Calderone
 
23
# Jessica McKellar
 
24
# Jonathan Jacobs
 
25
# Jonathan Lange
 
26
# Jonathan D. Simms
 
27
# Jürgen Hermann
 
28
# Kevin Horn
 
29
# Kevin Turner
 
30
# Mary Gardiner
 
31
# Matthew Lefkowitz
 
32
# Massachusetts Institute of Technology
 
33
# Moshe Zadka
 
34
# Paul Swartz
 
35
# Pavel Pergamenshchik
 
36
# Ralph Meijer
 
37
# Sean Riley
 
38
# Software Freedom Conservancy
 
39
# Travis B. Hartwell
 
40
# Thijs Triemstra
 
41
# Thomas Herve
 
42
# Timothy Allen
 
43
#
 
44
# Permission is hereby granted, free of charge, to any person obtaining
 
45
# a copy of this software and associated documentation files (the
 
46
# "Software"), to deal in the Software without restriction, including
 
47
# without limitation the rights to use, copy, modify, merge, publish,
 
48
# distribute, sublicense, and/or sell copies of the Software, and to
 
49
# permit persons to whom the Software is furnished to do so, subject to
 
50
# the following conditions:
 
51
#
 
52
# The above copyright notice and this permission notice shall be
 
53
# included in all copies or substantial portions of the Software.
 
54
#
 
55
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
56
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
57
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
58
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
59
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
60
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
61
# THE SOFTWARE.
19
62
 
20
63
__author__  = "Sebastian Heinlein <devel@glatzor.de>"
21
64
 
 
65
__all__ = ("Deferred", "AlreadyCalledDeferred", "DeferredException",
 
66
           "defer", "dbus_deferred_method", "inline_callbacks", "return_value")
 
67
 
22
68
from functools import wraps
23
69
import sys
24
70
 
25
71
import dbus
26
72
 
 
73
 
 
74
class _DefGen_Return(BaseException):
 
75
    """Exception to return a result from an inline callback."""
 
76
    def __init__(self, value):
 
77
        self.value = value
 
78
 
 
79
 
27
80
class AlreadyCalledDeferred(Exception):
28
81
    """The Deferred is already running a callback."""
29
82
 
286
339
        """
287
340
        if self.called:
288
341
            raise AlreadyCalledDeferred()
289
 
        if not isinstance(error, DeferredException):
 
342
        if not error:
 
343
            error = DeferredException()
 
344
        elif not isinstance(error, DeferredException):
290
345
            assert isinstance(error, Exception)
291
346
            error = DeferredException(error.__class__, error, None)
292
347
        self.called = True
414
469
        return wrapper
415
470
    return decorator
416
471
 
 
472
def deferable_function(func):
 
473
    """Add a defer attribute to the decorated function. If the decorated
 
474
    function has got a reply_handler and error_handler return a Deferred
 
475
    and use its callback and errback as reply_handler and error_handler.
 
476
 
 
477
    This decorator allows to easily make use of Deferreds in a DBus client.
 
478
    """
 
479
    def _deferable_function(*args, **kwargs):
 
480
        if kwargs.pop("defer", False):
 
481
            if "reply_handler" in kwargs and "error_handler" in kwargs:
 
482
                deferred = Deferred()
 
483
                kwargs["reply_handler"] = deferred.callback
 
484
                kwargs["error_handler"] = deferred.errback
 
485
                function(*args, **kwargs)
 
486
            else:
 
487
                deferred = defer(func, *args, **kwargs)
 
488
            return deferred
 
489
        return func(*args, **kwargs)
 
490
    return _deferable_function
 
491
 
417
492
def _passthrough(arg):
418
493
    return arg
419
494
 
 
495
def return_value(val):
 
496
    """
 
497
    Return val from a inline_callbacks generator.
 
498
 
 
499
    Note: this is currently implemented by raising an exception
 
500
    derived from BaseException.  You might want to change any
 
501
    'except:' clauses to an 'except Exception:' clause so as not to
 
502
    catch this exception.
 
503
 
 
504
    Also: while this function currently will work when called from
 
505
    within arbitrary functions called from within the generator, do
 
506
    not rely upon this behavior.
 
507
    """
 
508
    raise _DefGen_Return(val)
 
509
 
 
510
def _inline_callbacks(result, gen, deferred):
 
511
    """
 
512
    See inlineCallbacks.
 
513
    """
 
514
    # This function is complicated by the need to prevent unbounded recursion
 
515
    # arising from repeatedly yielding immediately ready deferreds.  This while
 
516
    # loop and the waiting variable solve that by manually unfolding the
 
517
    # recursion.
 
518
 
 
519
    waiting = [True, # waiting for result?
 
520
               None] # result
 
521
 
 
522
    while 1:
 
523
        try:
 
524
            # Send the last result back as the result of the yield expression.
 
525
            is_failure = isinstance(result, DeferredException)
 
526
            if is_failure:
 
527
                result = gen.throw(result.type, result.value, result.traceback)
 
528
            else:
 
529
                result = gen.send(result)
 
530
        except StopIteration:
 
531
            # fell off the end, or "return" statement
 
532
            deferred.callback(None)
 
533
            return deferred
 
534
        except _DefGen_Return, err:
 
535
            # returnValue() was called; time to give a result to the original
 
536
            # Deferred.  First though, let's try to identify the potentially
 
537
            # confusing situation which results when return_value() is
 
538
            # accidentally invoked from a different function, one that wasn't
 
539
            # decorated with @inline_callbacks.
 
540
 
 
541
            # The traceback starts in this frame (the one for
 
542
            # _inline_callbacks); the next one down should be the application
 
543
            # code.
 
544
            appCodeTrace = sys.exc_info()[2].tb_next
 
545
            if is_failure:
 
546
                # If we invoked this generator frame by throwing an exception
 
547
                # into it, then throwExceptionIntoGenerator will consume an
 
548
                # additional stack frame itself, so we need to skip that too.
 
549
                appCodeTrace = appCodeTrace.tb_next
 
550
            # Now that we've identified the frame being exited by the
 
551
            # exception, let's figure out if returnValue was called from it
 
552
            # directly.  returnValue itself consumes a stack frame, so the
 
553
            # application code will have a tb_next, but it will *not* have a
 
554
            # second tb_next.
 
555
            if appCodeTrace.tb_next.tb_next:
 
556
                # If returnValue was invoked non-local to the frame which it is
 
557
                # exiting, identify the frame that ultimately invoked
 
558
                # returnValue so that we can warn the user, as this behavior is
 
559
                # confusing.
 
560
                ultimateTrace = appCodeTrace
 
561
                while ultimateTrace.tb_next.tb_next:
 
562
                    ultimateTrace = ultimateTrace.tb_next
 
563
                filename = ultimateTrace.tb_frame.f_code.co_filename
 
564
                lineno = ultimateTrace.tb_lineno
 
565
                warnings.warn_explicit(
 
566
                    "returnValue() in %r causing %r to exit: "
 
567
                    "returnValue should only be invoked by functions decorated "
 
568
                    "with inlineCallbacks" % (
 
569
                        ultimateTrace.tb_frame.f_code.co_name,
 
570
                        appCodeTrace.tb_frame.f_code.co_name),
 
571
                    DeprecationWarning, filename, lineno)
 
572
            deferred.callback(err.value)
 
573
            return deferred
 
574
        except:
 
575
            deferred.errback()
 
576
            return deferred
 
577
 
 
578
        if isinstance(result, Deferred):
 
579
            # a deferred was yielded, get the result.
 
580
            def gotResult(res):
 
581
                if waiting[0]:
 
582
                    waiting[0] = False
 
583
                    waiting[1] = res
 
584
                else:
 
585
                    _inline_callbacks(res, gen, deferred)
 
586
 
 
587
            result.add_callbacks(gotResult, gotResult)
 
588
            if waiting[0]:
 
589
                # Haven't called back yet, set flag so that we get reinvoked
 
590
                # and return from the loop
 
591
                waiting[0] = False
 
592
                return deferred
 
593
 
 
594
            result = waiting[1]
 
595
            # Reset waiting to initial values for next loop.  gotResult uses
 
596
            # waiting, but this isn't a problem because gotResult is only
 
597
            # executed once, and if it hasn't been executed yet, the return
 
598
            # branch above would have been taken.
 
599
            waiting[0] = True
 
600
            waiting[1] = None
 
601
    return deferred
 
602
 
 
603
def inline_callbacks(func):
 
604
    """inline_callbacks helps you write Deferred-using code that looks like a
 
605
    regular sequential function. For example:
 
606
 
 
607
        def thingummy():
 
608
            thing = yield makeSomeRequestResultingInDeferred()
 
609
            print thing #the result! hoorj!
 
610
        thingummy = inline_callbacks(thingummy)
 
611
 
 
612
    When you call anything that results in a Deferred, you can simply yield it;
 
613
    your generator will automatically be resumed when the Deferred's result is
 
614
    available. The generator will be sent the result of the Deferred with the
 
615
    'send' method on generators, or if the result was a failure, 'throw'.
 
616
 
 
617
    Your inline_callbacks-enabled generator will return a Deferred object, which
 
618
    will result in the return value of the generator (or will fail with a
 
619
    failure object if your generator raises an unhandled exception). Note that
 
620
    you can't use return result to return a value; use return_value(result)
 
621
    instead. Falling off the end of the generator, or simply using return
 
622
    will cause the Deferred to have a result of None.
 
623
 
 
624
    The Deferred returned from your deferred generator may errback if your
 
625
    generator raised an exception:
 
626
 
 
627
        def thingummy():
 
628
            thing = yield makeSomeRequestResultingInDeferred()
 
629
            if thing == 'I love Twisted':
 
630
                # will become the result of the Deferred
 
631
                return_value('TWISTED IS GREAT!')
 
632
            else:
 
633
                # will trigger an errback
 
634
                raise Exception('DESTROY ALL LIFE')
 
635
        thingummy = inline_callbacks(thingummy)
 
636
    """
 
637
    @wraps(func)
 
638
    def unwind_generator(*args, **kwargs):
 
639
        return _inline_callbacks(None, func(*args, **kwargs), Deferred())
 
640
    return unwind_generator
 
641
 
 
642
 
420
643
# vim:tw=4:sw=4:et