MCPcopy Create free account
hub / github.com/pyinvoke/invoke / Responder

Class Responder

invoke/watchers.py:53–110  ·  view source on GitHub ↗

A parameterizable object that submits responses to specific patterns. Commonly used to implement password auto-responds for things like ``sudo``. .. versionadded:: 1.0

Source from the content-addressed store, hash-verified

51
52
53class Responder(StreamWatcher):
54 """
55 A parameterizable object that submits responses to specific patterns.
56
57 Commonly used to implement password auto-responds for things like ``sudo``.
58
59 .. versionadded:: 1.0
60 """
61
62 def __init__(self, pattern: str, response: str) -> None:
63 r"""
64 Imprint this `Responder` with necessary parameters.
65
66 :param pattern:
67 A raw string (e.g. ``r"\[sudo\] password for .*:"``) which will be
68 turned into a regular expression.
69
70 :param response:
71 The string to submit to the subprocess' stdin when ``pattern`` is
72 detected.
73 """
74 # TODO: precompile the keys into regex objects
75 self.pattern = pattern
76 self.response = response
77 self.index = 0
78
79 def pattern_matches(
80 self, stream: str, pattern: str, index_attr: str
81 ) -> Iterable[str]:
82 """
83 Generic "search for pattern in stream, using index" behavior.
84
85 Used here and in some subclasses that want to track multiple patterns
86 concurrently.
87
88 :param str stream: The same data passed to ``submit``.
89 :param str pattern: The pattern to search for.
90 :param str index_attr: The name of the index attribute to use.
91 :returns: An iterable of string matches.
92
93 .. versionadded:: 1.0
94 """
95 # NOTE: generifies scanning so it can be used to scan for >1 pattern at
96 # once, e.g. in FailingResponder.
97 # Only look at stream contents we haven't seen yet, to avoid dupes.
98 index = getattr(self, index_attr)
99 new = stream[index:]
100 # Search, across lines if necessary
101 matches = re.findall(pattern, new, re.S)
102 # Update seek index if we've matched
103 if matches:
104 setattr(self, index_attr, index + len(new))
105 return matches
106
107 def submit(self, stream: str) -> Generator[str, None, None]:
108 # Iterate over findall() response in case >1 match occurred.
109 for _ in self.pattern_matches(stream, self.pattern, "index"):
110 yield self.response

Calls

no outgoing calls

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…