py-rattler 0.22.0__cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
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.
- py_rattler-0.22.0.dist-info/METADATA +208 -0
- py_rattler-0.22.0.dist-info/RECORD +68 -0
- py_rattler-0.22.0.dist-info/WHEEL +4 -0
- rattler/__init__.py +114 -0
- rattler/channel/__init__.py +5 -0
- rattler/channel/channel.py +94 -0
- rattler/channel/channel_config.py +43 -0
- rattler/channel/channel_priority.py +14 -0
- rattler/exceptions.py +120 -0
- rattler/explicit_environment/__init__.py +3 -0
- rattler/explicit_environment/environment.py +69 -0
- rattler/index/__init__.py +3 -0
- rattler/index/index.py +112 -0
- rattler/install/__init__.py +3 -0
- rattler/install/installer.py +96 -0
- rattler/lock/__init__.py +23 -0
- rattler/lock/channel.py +52 -0
- rattler/lock/environment.py +213 -0
- rattler/lock/hash.py +33 -0
- rattler/lock/lock_file.py +118 -0
- rattler/lock/package.py +302 -0
- rattler/match_spec/__init__.py +4 -0
- rattler/match_spec/match_spec.py +294 -0
- rattler/match_spec/nameless_match_spec.py +185 -0
- rattler/networking/__init__.py +21 -0
- rattler/networking/client.py +74 -0
- rattler/networking/fetch_repo_data.py +103 -0
- rattler/networking/middleware.py +234 -0
- rattler/package/__init__.py +26 -0
- rattler/package/about_json.py +329 -0
- rattler/package/index_json.py +437 -0
- rattler/package/no_arch_type.py +142 -0
- rattler/package/package_name.py +204 -0
- rattler/package/package_name_matcher.py +81 -0
- rattler/package/paths_json.py +696 -0
- rattler/package/run_exports_json.py +268 -0
- rattler/package_streaming/__init__.py +26 -0
- rattler/platform/__init__.py +4 -0
- rattler/platform/arch.py +59 -0
- rattler/platform/platform.py +217 -0
- rattler/prefix/__init__.py +4 -0
- rattler/prefix/prefix_paths.py +442 -0
- rattler/prefix/prefix_record.py +234 -0
- rattler/pty/__init__.py +25 -0
- rattler/pty/pty_process.py +391 -0
- rattler/pty/pty_session.py +241 -0
- rattler/py.typed +0 -0
- rattler/rattler.abi3.so +0 -0
- rattler/repo_data/__init__.py +19 -0
- rattler/repo_data/gateway.py +337 -0
- rattler/repo_data/package_record.py +938 -0
- rattler/repo_data/patch_instructions.py +22 -0
- rattler/repo_data/record.py +164 -0
- rattler/repo_data/repo_data.py +74 -0
- rattler/repo_data/source.py +85 -0
- rattler/repo_data/sparse.py +356 -0
- rattler/shell/__init__.py +3 -0
- rattler/shell/shell.py +134 -0
- rattler/solver/__init__.py +3 -0
- rattler/solver/solver.py +220 -0
- rattler/utils/rattler_version.py +19 -0
- rattler/version/__init__.py +5 -0
- rattler/version/version.py +591 -0
- rattler/version/version_spec.py +184 -0
- rattler/version/with_source.py +80 -0
- rattler/virtual_package/__init__.py +4 -0
- rattler/virtual_package/generic.py +136 -0
- rattler/virtual_package/virtual_package.py +201 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import BinaryIO, Optional
|
|
5
|
+
|
|
6
|
+
# Try to import PTY classes - they may not be available on Windows or without pty feature
|
|
7
|
+
try:
|
|
8
|
+
from rattler.rattler import PyPtyProcess, PyPtyProcessOptions
|
|
9
|
+
|
|
10
|
+
_PTY_AVAILABLE = True
|
|
11
|
+
except (ImportError, AttributeError):
|
|
12
|
+
_PTY_AVAILABLE = False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PtyProcessOptions:
|
|
16
|
+
"""
|
|
17
|
+
Options for creating a PTY process.
|
|
18
|
+
|
|
19
|
+
Controls behavior like whether input is echoed back to the terminal.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
_inner: PyPtyProcessOptions
|
|
23
|
+
|
|
24
|
+
def __init__(self, echo: bool = True) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Create options for a PTY process.
|
|
27
|
+
|
|
28
|
+
Arguments:
|
|
29
|
+
echo: Whether to echo input back to the terminal. Defaults to True.
|
|
30
|
+
|
|
31
|
+
Examples
|
|
32
|
+
--------
|
|
33
|
+
```python
|
|
34
|
+
>>> from rattler.pty import PtyProcessOptions
|
|
35
|
+
>>> # Create with echo enabled (default)
|
|
36
|
+
>>> opts = PtyProcessOptions()
|
|
37
|
+
>>> opts.echo
|
|
38
|
+
True
|
|
39
|
+
>>> # Create with echo disabled
|
|
40
|
+
>>> opts = PtyProcessOptions(echo=False)
|
|
41
|
+
>>> opts.echo
|
|
42
|
+
False
|
|
43
|
+
>>>
|
|
44
|
+
```
|
|
45
|
+
"""
|
|
46
|
+
if not _PTY_AVAILABLE:
|
|
47
|
+
raise ImportError("PTY functionality is not available on this platform")
|
|
48
|
+
self._inner = PyPtyProcessOptions(echo)
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def _from_py_pty_process_options(cls, py_pty_process_options: PyPtyProcessOptions) -> PtyProcessOptions:
|
|
52
|
+
"""Construct from FFI PyPtyProcessOptions object."""
|
|
53
|
+
opts = cls.__new__(cls)
|
|
54
|
+
opts._inner = py_pty_process_options
|
|
55
|
+
return opts
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def echo(self) -> bool:
|
|
59
|
+
"""Whether input is echoed back to the terminal."""
|
|
60
|
+
return self._inner.echo
|
|
61
|
+
|
|
62
|
+
def __repr__(self) -> str:
|
|
63
|
+
return f"PtyProcessOptions(echo={self.echo})"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class PtyProcess:
|
|
67
|
+
"""
|
|
68
|
+
A pseudoterminal (PTY) process.
|
|
69
|
+
|
|
70
|
+
This is the lower-level PTY API that gives you more control over the process.
|
|
71
|
+
Use this when you need to:
|
|
72
|
+
- Read/write to the PTY manually using file handles
|
|
73
|
+
- Check process status
|
|
74
|
+
- Control process termination with specific signals
|
|
75
|
+
|
|
76
|
+
For interactive shell sessions, consider using `PtySession` instead, which
|
|
77
|
+
provides higher-level conveniences like `send_line()` and `interact()`.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
_inner: PyPtyProcess
|
|
81
|
+
|
|
82
|
+
def __init__(self, command: list[str], options: Optional[PtyProcessOptions] = None) -> None:
|
|
83
|
+
"""
|
|
84
|
+
Create a new PTY process with the given command.
|
|
85
|
+
|
|
86
|
+
Arguments:
|
|
87
|
+
command: A list of strings representing the command and its arguments.
|
|
88
|
+
The first element is the executable, subsequent elements are arguments.
|
|
89
|
+
options: Optional PtyProcessOptions to configure the PTY behavior.
|
|
90
|
+
If not provided, defaults to echo=True.
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
RuntimeError: If the PTY process could not be created.
|
|
94
|
+
|
|
95
|
+
Examples
|
|
96
|
+
--------
|
|
97
|
+
```python
|
|
98
|
+
>>> from rattler.pty import PtyProcess, PtyProcessOptions
|
|
99
|
+
>>> # Create with default options (echo enabled)
|
|
100
|
+
>>> process = PtyProcess(["bash"])
|
|
101
|
+
>>> # Create with custom options
|
|
102
|
+
>>> opts = PtyProcessOptions(echo=False)
|
|
103
|
+
>>> process = PtyProcess(["bash", "-l"], opts)
|
|
104
|
+
>>> # Check process status
|
|
105
|
+
>>> status = process.status()
|
|
106
|
+
>>> print(status)
|
|
107
|
+
StillAlive
|
|
108
|
+
>>>
|
|
109
|
+
```
|
|
110
|
+
"""
|
|
111
|
+
if not _PTY_AVAILABLE:
|
|
112
|
+
raise ImportError("PTY functionality is not available on this platform")
|
|
113
|
+
if options is None:
|
|
114
|
+
self._inner = PyPtyProcess(command)
|
|
115
|
+
else:
|
|
116
|
+
self._inner = PyPtyProcess(command, options._inner)
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def _from_py_pty_process(cls, py_pty_process: PyPtyProcess) -> PtyProcess:
|
|
120
|
+
"""Construct from FFI PyPtyProcess object."""
|
|
121
|
+
process = cls.__new__(cls)
|
|
122
|
+
process._inner = py_pty_process
|
|
123
|
+
return process
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def child_pid(self) -> int:
|
|
127
|
+
"""
|
|
128
|
+
Get the process ID (PID) of the child process.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
The PID as an integer.
|
|
132
|
+
|
|
133
|
+
Examples
|
|
134
|
+
--------
|
|
135
|
+
```python
|
|
136
|
+
>>> process = PtyProcess(["bash"])
|
|
137
|
+
>>> pid = process.child_pid
|
|
138
|
+
>>> print(f"Process ID: {pid}")
|
|
139
|
+
Process ID: 12345
|
|
140
|
+
>>>
|
|
141
|
+
```
|
|
142
|
+
"""
|
|
143
|
+
return self._inner.child_pid
|
|
144
|
+
|
|
145
|
+
def status(self) -> Optional[str]:
|
|
146
|
+
"""
|
|
147
|
+
Check the status of the child process (non-blocking).
|
|
148
|
+
|
|
149
|
+
This runs waitpid() with WNOHANG, so it returns immediately.
|
|
150
|
+
Note: If you previously called exit() or status() returned an exit status,
|
|
151
|
+
subsequent calls may return None.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
A string representing the process status, or None if status cannot be determined.
|
|
155
|
+
Possible values:
|
|
156
|
+
- "StillAlive": Process is still running
|
|
157
|
+
- "Exited(code)": Process exited with the given exit code
|
|
158
|
+
- "Signaled(signal)": Process was terminated by a signal
|
|
159
|
+
- "Stopped": Process was stopped
|
|
160
|
+
|
|
161
|
+
Examples
|
|
162
|
+
--------
|
|
163
|
+
```python
|
|
164
|
+
>>> import time
|
|
165
|
+
>>> process = PtyProcess(["sleep", "10"])
|
|
166
|
+
>>> print(process.status())
|
|
167
|
+
StillAlive
|
|
168
|
+
>>> time.sleep(11)
|
|
169
|
+
>>> print(process.status())
|
|
170
|
+
Exited(0)
|
|
171
|
+
>>>
|
|
172
|
+
```
|
|
173
|
+
"""
|
|
174
|
+
return self._inner.status()
|
|
175
|
+
|
|
176
|
+
def exit(self) -> str:
|
|
177
|
+
"""
|
|
178
|
+
Exit the process gracefully by sending SIGTERM.
|
|
179
|
+
|
|
180
|
+
This method blocks until the process has exited. If the process doesn't
|
|
181
|
+
respond to SIGTERM, it will eventually be killed with SIGKILL if a
|
|
182
|
+
kill_timeout was set (not currently exposed to Python).
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
A string describing the exit status.
|
|
186
|
+
|
|
187
|
+
Raises:
|
|
188
|
+
RuntimeError: If the process could not be terminated.
|
|
189
|
+
|
|
190
|
+
Examples
|
|
191
|
+
--------
|
|
192
|
+
```python
|
|
193
|
+
>>> process = PtyProcess(["bash"])
|
|
194
|
+
>>> status = process.exit()
|
|
195
|
+
>>> print(status)
|
|
196
|
+
Exited(0)
|
|
197
|
+
>>>
|
|
198
|
+
```
|
|
199
|
+
"""
|
|
200
|
+
return self._inner.exit()
|
|
201
|
+
|
|
202
|
+
def get_file_handle(self) -> BinaryIO:
|
|
203
|
+
"""
|
|
204
|
+
Get a file handle for reading from and writing to the PTY.
|
|
205
|
+
|
|
206
|
+
This returns a Python file-like object that can be used to read output
|
|
207
|
+
from the process and write input to it. This is useful for non-interactive
|
|
208
|
+
automation where you want to programmatically read the process output.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
A Python binary file object for reading/writing to the PTY.
|
|
212
|
+
|
|
213
|
+
Raises:
|
|
214
|
+
RuntimeError: If the file handle could not be created.
|
|
215
|
+
|
|
216
|
+
Examples
|
|
217
|
+
--------
|
|
218
|
+
```python
|
|
219
|
+
>>> process = PtyProcess(["bash"])
|
|
220
|
+
>>> file = process.get_file_handle()
|
|
221
|
+
>>> # Write to the process
|
|
222
|
+
>>> file.write(b"echo hello\\n")
|
|
223
|
+
>>> file.flush()
|
|
224
|
+
>>> # Read output (this is a blocking operation)
|
|
225
|
+
>>> output = file.read(100)
|
|
226
|
+
>>> print(output)
|
|
227
|
+
>>>
|
|
228
|
+
```
|
|
229
|
+
"""
|
|
230
|
+
fd = self._inner.get_file_handle()
|
|
231
|
+
return os.fdopen(fd, "r+b", buffering=0)
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def kill_timeout(self) -> Optional[float]:
|
|
235
|
+
"""
|
|
236
|
+
Get the kill timeout in seconds.
|
|
237
|
+
|
|
238
|
+
When calling exit() or async_exit(), if the process doesn't respond to
|
|
239
|
+
SIGTERM within this timeout, it will be forcefully killed with SIGKILL.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
The timeout in seconds, or None if no timeout is set.
|
|
243
|
+
|
|
244
|
+
Examples
|
|
245
|
+
--------
|
|
246
|
+
```python
|
|
247
|
+
>>> process = PtyProcess(["bash"])
|
|
248
|
+
>>> print(process.kill_timeout)
|
|
249
|
+
None
|
|
250
|
+
>>>
|
|
251
|
+
```
|
|
252
|
+
"""
|
|
253
|
+
return self._inner.get_kill_timeout()
|
|
254
|
+
|
|
255
|
+
@kill_timeout.setter
|
|
256
|
+
def kill_timeout(self, timeout: Optional[float]) -> None:
|
|
257
|
+
"""
|
|
258
|
+
Set the kill timeout in seconds.
|
|
259
|
+
|
|
260
|
+
Arguments:
|
|
261
|
+
timeout: Timeout in seconds, or None to disable timeout.
|
|
262
|
+
|
|
263
|
+
Examples
|
|
264
|
+
--------
|
|
265
|
+
```python
|
|
266
|
+
>>> process = PtyProcess(["bash"])
|
|
267
|
+
>>> process.kill_timeout = 5.0 # 5 second timeout
|
|
268
|
+
>>> process.kill_timeout = None # Disable timeout
|
|
269
|
+
>>>
|
|
270
|
+
```
|
|
271
|
+
"""
|
|
272
|
+
self._inner.set_kill_timeout(timeout)
|
|
273
|
+
|
|
274
|
+
async def async_read(self, size: int = 8192) -> bytes:
|
|
275
|
+
"""
|
|
276
|
+
Read from the PTY asynchronously.
|
|
277
|
+
|
|
278
|
+
This is the async version of reading via get_file_handle(). It performs
|
|
279
|
+
non-blocking I/O using tokio on the Rust side, bridged to Python's asyncio.
|
|
280
|
+
|
|
281
|
+
Arguments:
|
|
282
|
+
size: Maximum number of bytes to read. Defaults to 8192.
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
Bytes read from the PTY.
|
|
286
|
+
|
|
287
|
+
Raises:
|
|
288
|
+
RuntimeError: If the read operation fails.
|
|
289
|
+
|
|
290
|
+
Examples
|
|
291
|
+
--------
|
|
292
|
+
```python
|
|
293
|
+
>>> import asyncio
|
|
294
|
+
>>> async def main():
|
|
295
|
+
... process = PtyProcess(["bash", "-c", "echo hello"])
|
|
296
|
+
... data = await process.async_read(1024)
|
|
297
|
+
... print(data)
|
|
298
|
+
>>> asyncio.run(main())
|
|
299
|
+
>>>
|
|
300
|
+
```
|
|
301
|
+
"""
|
|
302
|
+
return await self._inner.async_read(size)
|
|
303
|
+
|
|
304
|
+
async def async_write(self, data: bytes) -> int:
|
|
305
|
+
"""
|
|
306
|
+
Write to the PTY asynchronously.
|
|
307
|
+
|
|
308
|
+
This is the async version of writing via get_file_handle(). It performs
|
|
309
|
+
non-blocking I/O using tokio on the Rust side.
|
|
310
|
+
|
|
311
|
+
Arguments:
|
|
312
|
+
data: Bytes to write to the PTY.
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
The number of bytes written.
|
|
316
|
+
|
|
317
|
+
Raises:
|
|
318
|
+
RuntimeError: If the write operation fails.
|
|
319
|
+
|
|
320
|
+
Examples
|
|
321
|
+
--------
|
|
322
|
+
```python
|
|
323
|
+
>>> import asyncio
|
|
324
|
+
>>> async def main():
|
|
325
|
+
... process = PtyProcess(["cat"])
|
|
326
|
+
... n = await process.async_write(b"hello\\n")
|
|
327
|
+
... print(f"Wrote {n} bytes")
|
|
328
|
+
... process.exit()
|
|
329
|
+
>>> asyncio.run(main())
|
|
330
|
+
>>>
|
|
331
|
+
```
|
|
332
|
+
"""
|
|
333
|
+
return await self._inner.async_write(data)
|
|
334
|
+
|
|
335
|
+
async def async_wait(self) -> str:
|
|
336
|
+
"""
|
|
337
|
+
Wait for the process to exit asynchronously.
|
|
338
|
+
|
|
339
|
+
This is the async version of polling status() in a loop. It polls the
|
|
340
|
+
process status periodically without blocking the async runtime.
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
A string describing the exit status.
|
|
344
|
+
|
|
345
|
+
Raises:
|
|
346
|
+
RuntimeError: If waiting fails.
|
|
347
|
+
|
|
348
|
+
Examples
|
|
349
|
+
--------
|
|
350
|
+
```python
|
|
351
|
+
>>> import asyncio
|
|
352
|
+
>>> async def main():
|
|
353
|
+
... process = PtyProcess(["sleep", "1"])
|
|
354
|
+
... status = await process.async_wait()
|
|
355
|
+
... print(status)
|
|
356
|
+
>>> asyncio.run(main())
|
|
357
|
+
Exited(0)
|
|
358
|
+
>>>
|
|
359
|
+
```
|
|
360
|
+
"""
|
|
361
|
+
return await self._inner.async_wait()
|
|
362
|
+
|
|
363
|
+
async def async_exit(self) -> str:
|
|
364
|
+
"""
|
|
365
|
+
Exit the process gracefully asynchronously by sending SIGTERM.
|
|
366
|
+
|
|
367
|
+
This is the async version of exit(). It sends SIGTERM and waits for
|
|
368
|
+
the process to terminate without blocking the async runtime.
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
A string describing the exit status.
|
|
372
|
+
|
|
373
|
+
Raises:
|
|
374
|
+
RuntimeError: If the process could not be terminated.
|
|
375
|
+
|
|
376
|
+
Examples
|
|
377
|
+
--------
|
|
378
|
+
```python
|
|
379
|
+
>>> import asyncio
|
|
380
|
+
>>> async def main():
|
|
381
|
+
... process = PtyProcess(["bash"])
|
|
382
|
+
... status = await process.async_exit()
|
|
383
|
+
... print(status)
|
|
384
|
+
>>> asyncio.run(main())
|
|
385
|
+
>>>
|
|
386
|
+
```
|
|
387
|
+
"""
|
|
388
|
+
return await self._inner.async_exit()
|
|
389
|
+
|
|
390
|
+
def __repr__(self) -> str:
|
|
391
|
+
return f"PtyProcess(child_pid={self.child_pid})"
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
# Try to import PTY classes - they may not be available on Windows or without pty feature
|
|
6
|
+
try:
|
|
7
|
+
from rattler.rattler import PyPtySession
|
|
8
|
+
|
|
9
|
+
_PTY_AVAILABLE = True
|
|
10
|
+
except (ImportError, AttributeError):
|
|
11
|
+
_PTY_AVAILABLE = False
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PtySession:
|
|
15
|
+
"""
|
|
16
|
+
A pseudoterminal (PTY) session for interactive shell use.
|
|
17
|
+
|
|
18
|
+
This is the higher-level PTY API built on top of PtyProcess.
|
|
19
|
+
It provides convenient methods for interactive shell sessions with:
|
|
20
|
+
- Easy command sending via send_line()
|
|
21
|
+
- Interactive mode with wait_until pattern matching
|
|
22
|
+
- Automatic buffering and flushing
|
|
23
|
+
|
|
24
|
+
Use this for interactive shell sessions where you want to send commands
|
|
25
|
+
and optionally hand over control to the user.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
_inner: PyPtySession
|
|
29
|
+
|
|
30
|
+
def __init__(self, command: list[str]) -> None:
|
|
31
|
+
"""
|
|
32
|
+
Create a new PTY session with the given command.
|
|
33
|
+
|
|
34
|
+
The PTY session is created with echo enabled by default, which is
|
|
35
|
+
appropriate for interactive shell use.
|
|
36
|
+
|
|
37
|
+
Arguments:
|
|
38
|
+
command: A list of strings representing the command and its arguments.
|
|
39
|
+
The first element is the executable, subsequent elements are arguments.
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
RuntimeError: If the PTY session could not be created.
|
|
43
|
+
|
|
44
|
+
Examples
|
|
45
|
+
--------
|
|
46
|
+
```python
|
|
47
|
+
>>> from rattler.pty import PtySession
|
|
48
|
+
>>> # Start an interactive bash session
|
|
49
|
+
>>> session = PtySession(["bash"])
|
|
50
|
+
>>> session.send_line("export MY_VAR=hello")
|
|
51
|
+
>>> # Hand over control to the user (blocks until shell exits)
|
|
52
|
+
>>> exit_code = session.interact()
|
|
53
|
+
>>>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If you want to send commands without using interact() (which blocks),
|
|
57
|
+
use exit() to clean up when done:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
>>> from rattler.pty import PtySession
|
|
61
|
+
>>> session = PtySession(["bash"])
|
|
62
|
+
>>> session.send_line("echo hello")
|
|
63
|
+
>>> status = session.exit() # Clean up the session
|
|
64
|
+
>>>
|
|
65
|
+
```
|
|
66
|
+
"""
|
|
67
|
+
if not _PTY_AVAILABLE:
|
|
68
|
+
raise ImportError("PTY functionality is not available on this platform")
|
|
69
|
+
self._inner = PyPtySession(command)
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def _from_py_pty_session(cls, py_pty_session: PyPtySession) -> PtySession:
|
|
73
|
+
"""Construct from FFI PyPtySession object."""
|
|
74
|
+
session = cls.__new__(cls)
|
|
75
|
+
session._inner = py_pty_session
|
|
76
|
+
return session
|
|
77
|
+
|
|
78
|
+
def send_line(self, line: str) -> int:
|
|
79
|
+
"""
|
|
80
|
+
Send a string followed by a newline to the PTY session.
|
|
81
|
+
|
|
82
|
+
This is like typing a command and pressing Enter. The command is flushed
|
|
83
|
+
immediately, so the shell will receive it right away.
|
|
84
|
+
|
|
85
|
+
Arguments:
|
|
86
|
+
line: The string to send (newline will be added automatically).
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
The number of bytes written.
|
|
90
|
+
|
|
91
|
+
Raises:
|
|
92
|
+
RuntimeError: If the write operation fails.
|
|
93
|
+
|
|
94
|
+
Examples
|
|
95
|
+
--------
|
|
96
|
+
```python
|
|
97
|
+
>>> from rattler.pty import PtySession
|
|
98
|
+
>>> session = PtySession(["bash"])
|
|
99
|
+
>>> session.send_line("export MY_VAR=hello")
|
|
100
|
+
22
|
|
101
|
+
>>> session.send_line("echo $MY_VAR")
|
|
102
|
+
13
|
|
103
|
+
>>> session.exit() # Clean up when done
|
|
104
|
+
'Signaled(SIGTERM)'
|
|
105
|
+
>>>
|
|
106
|
+
```
|
|
107
|
+
"""
|
|
108
|
+
return self._inner.send_line(line)
|
|
109
|
+
|
|
110
|
+
def send(self, data: str) -> int:
|
|
111
|
+
"""
|
|
112
|
+
Send a string to the PTY session without adding a newline.
|
|
113
|
+
|
|
114
|
+
Note: You'll need to call flush() to ensure the data is sent.
|
|
115
|
+
|
|
116
|
+
Arguments:
|
|
117
|
+
data: The string to send.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
The number of bytes written.
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
RuntimeError: If the write operation fails.
|
|
124
|
+
|
|
125
|
+
Examples
|
|
126
|
+
--------
|
|
127
|
+
```python
|
|
128
|
+
>>> from rattler.pty import PtySession
|
|
129
|
+
>>> session = PtySession(["bash"])
|
|
130
|
+
>>> session.send("echo")
|
|
131
|
+
4
|
|
132
|
+
>>> session.send(" hello\\n")
|
|
133
|
+
7
|
|
134
|
+
>>> session.flush()
|
|
135
|
+
>>> session.exit() # Clean up when done
|
|
136
|
+
'Signaled(SIGTERM)'
|
|
137
|
+
>>>
|
|
138
|
+
```
|
|
139
|
+
"""
|
|
140
|
+
return self._inner.send(data)
|
|
141
|
+
|
|
142
|
+
def flush(self) -> None:
|
|
143
|
+
"""
|
|
144
|
+
Flush any pending output to the PTY.
|
|
145
|
+
|
|
146
|
+
This is automatically called by send_line(), but can be called manually
|
|
147
|
+
if you use send().
|
|
148
|
+
|
|
149
|
+
Raises:
|
|
150
|
+
RuntimeError: If the flush operation fails.
|
|
151
|
+
|
|
152
|
+
Examples
|
|
153
|
+
--------
|
|
154
|
+
```python
|
|
155
|
+
>>> from rattler.pty import PtySession
|
|
156
|
+
>>> session = PtySession(["bash"])
|
|
157
|
+
>>> session.send("echo hello\\n")
|
|
158
|
+
11
|
|
159
|
+
>>> session.flush() # Make sure the command is sent
|
|
160
|
+
>>> session.exit() # Clean up when done
|
|
161
|
+
'Signaled(SIGTERM)'
|
|
162
|
+
>>>
|
|
163
|
+
```
|
|
164
|
+
"""
|
|
165
|
+
self._inner.flush()
|
|
166
|
+
|
|
167
|
+
def exit(self) -> str:
|
|
168
|
+
"""
|
|
169
|
+
Exit the process gracefully by sending SIGTERM.
|
|
170
|
+
|
|
171
|
+
This method blocks until the process has exited. Useful for cleaning up
|
|
172
|
+
PTY sessions when you're done sending commands but don't want to use interact().
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
A string describing the exit status.
|
|
176
|
+
|
|
177
|
+
Raises:
|
|
178
|
+
RuntimeError: If the process could not be terminated.
|
|
179
|
+
|
|
180
|
+
Examples
|
|
181
|
+
--------
|
|
182
|
+
```python
|
|
183
|
+
>>> from rattler.pty import PtySession
|
|
184
|
+
>>> session = PtySession(["bash"])
|
|
185
|
+
>>> session.send_line("echo hello")
|
|
186
|
+
11
|
|
187
|
+
>>> status = session.exit()
|
|
188
|
+
>>> print(status)
|
|
189
|
+
Signaled(SIGTERM)
|
|
190
|
+
>>>
|
|
191
|
+
```
|
|
192
|
+
"""
|
|
193
|
+
return self._inner.exit()
|
|
194
|
+
|
|
195
|
+
def interact(self, wait_until: Optional[str] = None) -> Optional[int]:
|
|
196
|
+
"""
|
|
197
|
+
Start an interactive session, optionally waiting for a pattern first.
|
|
198
|
+
|
|
199
|
+
This method:
|
|
200
|
+
1. Sets the terminal to raw mode
|
|
201
|
+
2. If wait_until is provided, buffers output until that pattern appears
|
|
202
|
+
3. Then forwards all I/O between the user's terminal and the PTY
|
|
203
|
+
4. Returns when the shell process exits
|
|
204
|
+
|
|
205
|
+
Arguments:
|
|
206
|
+
wait_until: Optional pattern to wait for before showing output.
|
|
207
|
+
Useful for waiting for shell initialization to complete.
|
|
208
|
+
If not found within 1 second, a warning is shown and
|
|
209
|
+
interaction begins anyway.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
The exit code of the shell process, or None if terminated by signal.
|
|
213
|
+
|
|
214
|
+
Raises:
|
|
215
|
+
RuntimeError: If interaction fails.
|
|
216
|
+
|
|
217
|
+
Examples
|
|
218
|
+
--------
|
|
219
|
+
```python
|
|
220
|
+
>>> session = PtySession(["bash"])
|
|
221
|
+
>>> # Send some setup commands
|
|
222
|
+
>>> session.send_line("export MY_VAR=hello")
|
|
223
|
+
>>> session.send_line("echo 'READY'")
|
|
224
|
+
>>> # Wait for "READY" before handing control to user
|
|
225
|
+
>>> exit_code = session.interact(wait_until="READY")
|
|
226
|
+
>>> print(f"Session exited with code: {exit_code}")
|
|
227
|
+
Session exited with code: 0
|
|
228
|
+
>>>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
>>> session = PtySession(["bash"])
|
|
233
|
+
>>> # Interact immediately without waiting
|
|
234
|
+
>>> exit_code = session.interact()
|
|
235
|
+
>>>
|
|
236
|
+
```
|
|
237
|
+
"""
|
|
238
|
+
return self._inner.interact(wait_until)
|
|
239
|
+
|
|
240
|
+
def __repr__(self) -> str:
|
|
241
|
+
return "PtySession()"
|
rattler/py.typed
ADDED
|
File without changes
|
rattler/rattler.abi3.so
ADDED
|
Binary file
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from rattler.repo_data.package_record import PackageRecord
|
|
2
|
+
from rattler.repo_data.repo_data import RepoData
|
|
3
|
+
from rattler.repo_data.patch_instructions import PatchInstructions
|
|
4
|
+
from rattler.repo_data.record import RepoDataRecord
|
|
5
|
+
from rattler.repo_data.sparse import SparseRepoData, PackageFormatSelection
|
|
6
|
+
from rattler.repo_data.gateway import Gateway, SourceConfig
|
|
7
|
+
from rattler.repo_data.source import RepoDataSource
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"PackageRecord",
|
|
11
|
+
"RepoData",
|
|
12
|
+
"PatchInstructions",
|
|
13
|
+
"RepoDataRecord",
|
|
14
|
+
"SparseRepoData",
|
|
15
|
+
"Gateway",
|
|
16
|
+
"SourceConfig",
|
|
17
|
+
"PackageFormatSelection",
|
|
18
|
+
"RepoDataSource",
|
|
19
|
+
]
|