shelltastic 0.8.0__tar.gz → 0.8.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {shelltastic-0.8.0 → shelltastic-0.8.2}/PKG-INFO +1 -1
- {shelltastic-0.8.0 → shelltastic-0.8.2}/pyproject.toml +1 -1
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/backend/__init__.py +2 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/backend/base.py +18 -2
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/backend/default/common.py +58 -45
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/exception.py +4 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/result.py +21 -6
- {shelltastic-0.8.0 → shelltastic-0.8.2}/LICENSE +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/README.md +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/__init__.py +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/backend/default/__init__.py +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/backend/default/local.py +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/backend/default/remote.py +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/display.py +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/enum.py +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/frontend/__init__.py +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/frontend/common.py +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/frontend/git.py +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/frontend/scp.py +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/frontend/shell.py +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/host.py +0 -0
- {shelltastic-0.8.0 → shelltastic-0.8.2}/src/shelltastic/io.py +0 -0
|
@@ -4,6 +4,7 @@ from shelltastic.backend.base import (
|
|
|
4
4
|
RemoteShellBackend,
|
|
5
5
|
ShellBackend,
|
|
6
6
|
add_shim,
|
|
7
|
+
clear_shims,
|
|
7
8
|
)
|
|
8
9
|
from shelltastic.backend.default.local import DefaultLocalBackend
|
|
9
10
|
from shelltastic.backend.default.remote import DefaultRemoteBackend
|
|
@@ -17,6 +18,7 @@ __all__ = [
|
|
|
17
18
|
"register_default_local_backend",
|
|
18
19
|
"register_default_remote_backend",
|
|
19
20
|
"add_shim",
|
|
21
|
+
"clear_shims",
|
|
20
22
|
"get_local_backend",
|
|
21
23
|
"get_remote_backend",
|
|
22
24
|
]
|
|
@@ -90,6 +90,10 @@ class BackendShim(ABC):
|
|
|
90
90
|
|
|
91
91
|
_backend_shims: list[BackendShim] = []
|
|
92
92
|
|
|
93
|
+
def clear_shims():
|
|
94
|
+
"""Clear all shims"""
|
|
95
|
+
global _backend_shims
|
|
96
|
+
_backend_shims.clear()
|
|
93
97
|
|
|
94
98
|
def add_shim(shim: BackendShim):
|
|
95
99
|
"""Add a shim to manipulate `ShellBackend` params before any `ShellBackend` runs"""
|
|
@@ -103,7 +107,19 @@ def _shim_run(f, backend_type: type):
|
|
|
103
107
|
result: BackendShimResult = BackendShimResult(*args, **kwargs)
|
|
104
108
|
for shim in _backend_shims:
|
|
105
109
|
result = shim.run_shim(
|
|
106
|
-
backend_type,
|
|
110
|
+
backend_type, *result.args, **result.kwargs
|
|
111
|
+
)
|
|
112
|
+
return f(*result.args, **result.kwargs)
|
|
113
|
+
|
|
114
|
+
return wrapper
|
|
115
|
+
|
|
116
|
+
def _shim_run_async(f, backend_type: type):
|
|
117
|
+
@wraps(f)
|
|
118
|
+
def wrapper(*args, **kwargs):
|
|
119
|
+
result: BackendShimResult = BackendShimResult(*args, **kwargs)
|
|
120
|
+
for shim in _backend_shims:
|
|
121
|
+
result = shim.run_async_shim(
|
|
122
|
+
backend_type, *result.args, **result.kwargs
|
|
107
123
|
)
|
|
108
124
|
return f(*result.args, **result.kwargs)
|
|
109
125
|
|
|
@@ -114,7 +130,7 @@ class _InstallShims(ABCMeta):
|
|
|
114
130
|
def __call__(self, *args: Any, **kwds: Any) -> Any:
|
|
115
131
|
instance = super().__call__(*args, **kwds)
|
|
116
132
|
instance.run = _shim_run(instance.run, instance.__class__)
|
|
117
|
-
instance.run_async =
|
|
133
|
+
instance.run_async = _shim_run_async(instance.run_async, instance.__class__)
|
|
118
134
|
return instance
|
|
119
135
|
|
|
120
136
|
|
|
@@ -6,13 +6,14 @@ import time
|
|
|
6
6
|
from io import RawIOBase
|
|
7
7
|
from multiprocessing import Pipe
|
|
8
8
|
from multiprocessing.connection import Connection
|
|
9
|
-
from multiprocessing.pool import ThreadPool
|
|
10
9
|
from pathlib import Path
|
|
10
|
+
from threading import Thread
|
|
11
11
|
from typing import IO, Literal
|
|
12
12
|
|
|
13
13
|
from shelltastic import display
|
|
14
14
|
from shelltastic.backend.base import ShellBackend
|
|
15
15
|
from shelltastic.enum import CaptureMode, DisplayMode
|
|
16
|
+
from shelltastic.exception import ShellClosedException
|
|
16
17
|
from shelltastic.io import PipeReader
|
|
17
18
|
from shelltastic.result import ShellResult, ShellResultAsync
|
|
18
19
|
|
|
@@ -90,24 +91,20 @@ class IOTimeoutStream:
|
|
|
90
91
|
def _output_and_pipe(
|
|
91
92
|
io: IO[bytes] | None, display: display.IODisplay | None, pipe: Connection | None
|
|
92
93
|
) -> None:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return
|
|
96
|
-
|
|
97
|
-
if pipe:
|
|
98
|
-
if display:
|
|
99
|
-
for line in IOTimeoutStream(io):
|
|
100
|
-
pipe.send_bytes(line)
|
|
101
|
-
display.printbytes(line)
|
|
102
|
-
for line in IOTimeoutStream(io):
|
|
103
|
-
pipe.send_bytes(line)
|
|
94
|
+
if io is None:
|
|
95
|
+
return
|
|
104
96
|
|
|
97
|
+
if pipe:
|
|
105
98
|
if display:
|
|
106
99
|
for line in IOTimeoutStream(io):
|
|
100
|
+
pipe.send_bytes(line)
|
|
107
101
|
display.printbytes(line)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
102
|
+
for line in IOTimeoutStream(io):
|
|
103
|
+
pipe.send_bytes(line)
|
|
104
|
+
|
|
105
|
+
if display:
|
|
106
|
+
for line in IOTimeoutStream(io):
|
|
107
|
+
display.printbytes(line)
|
|
111
108
|
|
|
112
109
|
|
|
113
110
|
def _determine_subprocess_output_mode(
|
|
@@ -168,7 +165,7 @@ class CommonShellAsyncResult(ShellResultAsync[RawIOBase | None, RawIOBase | None
|
|
|
168
165
|
stderr_display: display.IODisplay | None,
|
|
169
166
|
check: bool,
|
|
170
167
|
) -> None:
|
|
171
|
-
|
|
168
|
+
super().__init__(cmd)
|
|
172
169
|
_stdout_async_result = None
|
|
173
170
|
_stderr_async_result = None
|
|
174
171
|
|
|
@@ -176,66 +173,77 @@ class CommonShellAsyncResult(ShellResultAsync[RawIOBase | None, RawIOBase | None
|
|
|
176
173
|
# If stdout is PIPE, then setup reader
|
|
177
174
|
if stdout_capture == CaptureMode.PIPE:
|
|
178
175
|
stdout_rcv, stdout_snd = Pipe(False)
|
|
179
|
-
|
|
176
|
+
self._stdout = PipeReader(stdout_rcv)
|
|
180
177
|
else:
|
|
181
178
|
stdout_snd = None
|
|
182
|
-
|
|
179
|
+
self._stdout = None
|
|
183
180
|
|
|
184
181
|
if stdout_snd or stdout_display:
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
_stdout_async_result = _display_pool.apply_async(
|
|
189
|
-
_output_and_pipe,
|
|
182
|
+
_stdout_async_result = Thread(
|
|
183
|
+
target=_output_and_pipe,
|
|
190
184
|
args=[
|
|
191
185
|
popen.stdout,
|
|
192
186
|
stdout_display,
|
|
193
187
|
stdout_snd,
|
|
194
188
|
],
|
|
195
189
|
)
|
|
190
|
+
_stdout_async_result.start()
|
|
196
191
|
else:
|
|
197
|
-
|
|
192
|
+
self._stdout = None
|
|
198
193
|
|
|
199
194
|
if popen.stderr:
|
|
200
195
|
if stderr_capture == CaptureMode.PIPE:
|
|
201
196
|
stderr_rcv, stderr_snd = Pipe(False)
|
|
202
|
-
|
|
197
|
+
self._stderr = PipeReader(stderr_rcv)
|
|
203
198
|
else:
|
|
204
199
|
stderr_snd = None
|
|
205
|
-
|
|
200
|
+
self._stderr = None
|
|
206
201
|
|
|
207
202
|
if stderr_snd or stderr_display:
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
_stderr_async_result = _display_pool.apply_async(
|
|
212
|
-
_output_and_pipe,
|
|
203
|
+
_stderr_async_result = Thread(
|
|
204
|
+
target=_output_and_pipe,
|
|
213
205
|
args=[
|
|
214
206
|
popen.stderr,
|
|
215
207
|
stderr_display,
|
|
216
208
|
stderr_snd,
|
|
217
209
|
],
|
|
218
210
|
)
|
|
211
|
+
_stderr_async_result.start()
|
|
219
212
|
else:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
super().__init__(cmd, stdout, stderr)
|
|
213
|
+
self._stderr = None
|
|
223
214
|
|
|
224
|
-
self._display_pool = _display_pool
|
|
225
215
|
self._stdout_async_result = _stdout_async_result
|
|
226
216
|
self._stderr_async_result = _stderr_async_result
|
|
227
217
|
self._popen = popen
|
|
228
218
|
self._check = check
|
|
219
|
+
self._closed = False
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def stdout(self) -> RawIOBase | None:
|
|
223
|
+
if self._closed:
|
|
224
|
+
raise ShellClosedException(f"{self} is closed")
|
|
225
|
+
|
|
226
|
+
return self._stdout
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def stderr(self) -> RawIOBase | None:
|
|
230
|
+
if self._closed:
|
|
231
|
+
raise ShellClosedException(f"{self} is closed")
|
|
232
|
+
|
|
233
|
+
return self._stderr
|
|
229
234
|
|
|
230
235
|
def __exit__(self, exc_type, exc, tb):
|
|
231
|
-
|
|
232
|
-
|
|
236
|
+
self.close()
|
|
237
|
+
|
|
238
|
+
def close(self) -> None:
|
|
239
|
+
if self._closed:
|
|
240
|
+
return
|
|
233
241
|
|
|
234
|
-
#
|
|
235
|
-
self._popen.
|
|
242
|
+
# Terminate popen
|
|
243
|
+
self._popen.terminate()
|
|
236
244
|
|
|
237
|
-
|
|
238
|
-
|
|
245
|
+
# Wait for command to finish before closing everything
|
|
246
|
+
self.wait()
|
|
239
247
|
|
|
240
248
|
# Close stdout and stderr
|
|
241
249
|
if self.stdout is not None:
|
|
@@ -243,14 +251,19 @@ class CommonShellAsyncResult(ShellResultAsync[RawIOBase | None, RawIOBase | None
|
|
|
243
251
|
if self.stderr is not None:
|
|
244
252
|
self.stderr.close()
|
|
245
253
|
|
|
246
|
-
|
|
247
|
-
self.__exit__(None, None, None)
|
|
254
|
+
self._closed = True
|
|
248
255
|
|
|
249
256
|
def terminate(self) -> None:
|
|
257
|
+
if self._closed:
|
|
258
|
+
raise ShellClosedException(f"{self} is closed")
|
|
259
|
+
|
|
250
260
|
self._popen.terminate()
|
|
251
261
|
self.close()
|
|
252
262
|
|
|
253
263
|
def result(self) -> ShellResult[RawIOBase | None, RawIOBase | None]:
|
|
264
|
+
if self._closed:
|
|
265
|
+
raise ShellClosedException(f"{self} is closed")
|
|
266
|
+
|
|
254
267
|
# Get result
|
|
255
268
|
result = ShellResult(
|
|
256
269
|
cmd=self.cmd,
|
|
@@ -261,10 +274,10 @@ class CommonShellAsyncResult(ShellResultAsync[RawIOBase | None, RawIOBase | None
|
|
|
261
274
|
|
|
262
275
|
# Wait for our async readers to be complete
|
|
263
276
|
if self._stdout_async_result:
|
|
264
|
-
self._stdout_async_result.
|
|
277
|
+
self._stdout_async_result.join()
|
|
265
278
|
|
|
266
279
|
if self._stderr_async_result:
|
|
267
|
-
self._stderr_async_result.
|
|
280
|
+
self._stderr_async_result.join()
|
|
268
281
|
|
|
269
282
|
# Check if set
|
|
270
283
|
if self._check:
|
|
@@ -18,12 +18,18 @@ class ShellResult[SO: RawIOBase | bytes | None, SE: RawIOBase | bytes | None]:
|
|
|
18
18
|
"""If returncode is not 0, raise ShellException"""
|
|
19
19
|
if self.returncode != 0:
|
|
20
20
|
result = self.to_result_bytes()
|
|
21
|
-
raise ShellException(
|
|
21
|
+
raise ShellException(
|
|
22
|
+
self.cmd, self.returncode, result.stdout, result.stderr
|
|
23
|
+
)
|
|
22
24
|
|
|
23
25
|
def to_result_bytes(self) -> ShellResult[bytes | None, bytes | None]:
|
|
24
|
-
stdout =
|
|
26
|
+
stdout = (
|
|
27
|
+
self.stdout.readall() if isinstance(self.stdout, RawIOBase) else self.stdout
|
|
28
|
+
)
|
|
25
29
|
|
|
26
|
-
stderr =
|
|
30
|
+
stderr = (
|
|
31
|
+
self.stderr.readall() if isinstance(self.stderr, RawIOBase) else self.stderr
|
|
32
|
+
)
|
|
27
33
|
|
|
28
34
|
return ShellResult(self.cmd, self.returncode, stdout, stderr)
|
|
29
35
|
|
|
@@ -39,15 +45,24 @@ class ShellResultAsync[SO: RawIOBase | None, SE: RawIOBase | None](ABC):
|
|
|
39
45
|
stderr: Bytes stream for stderr
|
|
40
46
|
"""
|
|
41
47
|
|
|
42
|
-
def __init__(self, cmd: str | list[str]
|
|
48
|
+
def __init__(self, cmd: str | list[str]) -> None:
|
|
43
49
|
self.cmd = cmd
|
|
44
50
|
"""The running command"""
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
def __str__(self) -> str:
|
|
53
|
+
return f"{self.__class__.__name__}({self.cmd})"
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def stdout(self) -> SO:
|
|
47
58
|
"""Byte based IO stream for stdout"""
|
|
59
|
+
raise NotImplementedError()
|
|
48
60
|
|
|
49
|
-
|
|
61
|
+
@property
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def stderr(self) -> SE:
|
|
50
64
|
"""Byte based IO stream for stderr"""
|
|
65
|
+
raise NotImplementedError()
|
|
51
66
|
|
|
52
67
|
def __enter__(self):
|
|
53
68
|
return self
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|