fabric / fabric
1 6
import invoke
2

3 6
from .connection import Connection
4

5

6 6
class Task(invoke.Task):
7
    """
8
    Extends `invoke.tasks.Task` with knowledge of target hosts and similar.
9

10
    As `invoke.tasks.Task` relegates documentation responsibility to its `@task
11
    <invoke.tasks.task>` expression, so we relegate most details to our version
12
    of `@task <fabric.tasks.task>` - please see its docs for details.
13

14
    .. versionadded:: 2.1
15
    """
16

17 6
    def __init__(self, *args, **kwargs):
18
        # Pull out our own kwargs before hitting super, which will TypeError on
19
        # anything it doesn't know about.
20 6
        self.hosts = kwargs.pop("hosts", None)
21 6
        super(Task, self).__init__(*args, **kwargs)
22

23

24 6
def task(*args, **kwargs):
25
    """
26
    Wraps/extends Invoke's `@task <invoke.tasks.task>` with extra kwargs.
27

28
    See `the Invoke-level API docs <invoke.tasks.task>` for most details; this
29
    Fabric-specific implementation adds the following additional keyword
30
    arguments:
31

32
    :param hosts:
33
        An iterable of host-connection specifiers appropriate for eventually
34
        instantiating a `.Connection`. The existence of this argument will
35
        trigger automatic parameterization of the task when invoked from the
36
        CLI, similar to the behavior of :option:`--hosts`.
37

38
        .. note::
39
            This parameterization is "lower-level" than that driven by
40
            :option:`--hosts`: if a task decorated with this parameter is
41
            executed in a session where :option:`--hosts` was given, the
42
            CLI-driven value will win out.
43

44
        List members may be one of:
45

46
        - A string appropriate for being the first positional argument to
47
          `.Connection` - see its docs for details, but these are typically
48
          shorthand-only convenience strings like ``hostname.example.com`` or
49
          ``user@host:port``.
50
        - A dictionary appropriate for use as keyword arguments when
51
          instantiating a `.Connection`. Useful for values that don't mesh well
52
          with simple strings (e.g. statically defined IPv6 addresses) or to
53
          bake in more complex info (eg ``connect_timeout``, ``connect_kwargs``
54
          params like auth info, etc).
55

56
        These two value types *may* be mixed together in the same list, though
57
        we recommend that you keep things homogenous when possible, to avoid
58
        confusion when debugging.
59

60
        .. note::
61
            No automatic deduplication of values is performed; if you pass in
62
            multiple references to the same effective target host, the wrapped
63
            task will execute on that host multiple times (including making
64
            separate connections).
65

66
    .. versionadded:: 2.1
67
    """
68
    # Override klass to be our own Task, not Invoke's, unless somebody gave it
69
    # explicitly.
70 6
    kwargs.setdefault("klass", Task)
71 6
    return invoke.task(*args, **kwargs)
72

73

74 6
class ConnectionCall(invoke.Call):
75
    """
76
    Subclass of `invoke.tasks.Call` that generates `Connections <.Connection>`.
77
    """
78

79 6
    def __init__(self, *args, **kwargs):
80
        """
81
        Creates a new `.ConnectionCall`.
82

83
        Performs minor extensions to `~invoke.tasks.Call` -- see its docstring
84
        for most details. Only specific-to-subclass params are documented here.
85

86
        :param dict init_kwargs:
87
            Keyword arguments used to create a new `.Connection` when the
88
            wrapped task is executed. Default: ``None``.
89
        """
90 6
        init_kwargs = kwargs.pop("init_kwargs")  # , None)
91 6
        super(ConnectionCall, self).__init__(*args, **kwargs)
92 6
        self.init_kwargs = init_kwargs
93

94 6
    def clone_kwargs(self):
95
        # Extend superclass clone_kwargs to work in init_kwargs.
96
        # TODO: this pattern comes up a lot; is there a better way to handle it
97
        # without getting too crazy on the metaprogramming/over-engineering?
98
        # Maybe something attrs library can help with (re: declaring "These are
99
        # my bag-of-attributes attributes I want common stuff done to/with")
100 0
        kwargs = super(ConnectionCall, self).clone_kwargs()
101 0
        kwargs["init_kwargs"] = self.init_kwargs
102 0
        return kwargs
103

104 6
    def make_context(self, config):
105 6
        kwargs = self.init_kwargs
106
        # TODO: what about corner case of a decorator giving config in a hosts
107
        # kwarg member?! For now let's stomp on it, and then if somebody runs
108
        # into it, we can identify the use case & decide how best to deal.
109 6
        kwargs["config"] = config
110 6
        return Connection(**kwargs)
111

112 6
    def __repr__(self):
113 6
        ret = super(ConnectionCall, self).__repr__()
114 6
        if self.init_kwargs:
115 6
            ret = ret[:-1] + ", host='{}'>".format(self.init_kwargs["host"])
116 6
        return ret

Read our documentation on viewing source code .

Loading