11import os
2+ import signal
23import sys
34from contextlib import ExitStack , asynccontextmanager , contextmanager
5+ from datetime import timedelta
6+ from functools import partial
47from subprocess import Popen
58
69from anyio .from_thread import BlockingPortal , start_blocking_portal
@@ -46,6 +49,33 @@ def serve(root_device: Driver):
4649PROMPT_CWD = "\\ W"
4750
4851
52+ def lease_ending_handler (process : Popen , remaining_time ) -> None :
53+ """Lease ending handler to terminate a process when lease ends.
54+
55+ Args:
56+ process: The process to terminate
57+ remaining_time: Time remaining until lease expiration
58+ """
59+
60+ if remaining_time <= timedelta (0 ):
61+ try :
62+ process .send_signal (signal .SIGHUP )
63+ except (ProcessLookupError , OSError ):
64+ pass # Process already terminated
65+
66+
67+ def _run_process (
68+ cmd : list [str ],
69+ env : dict [str , str ],
70+ lease = None ,
71+ ) -> int :
72+ """Helper to run a process with an option to set a lease ending callback."""
73+ process = Popen (cmd , stdin = sys .stdin , stdout = sys .stdout , stderr = sys .stderr , env = env )
74+ if lease is not None :
75+ lease .lease_ending_callback = partial (lease_ending_handler , process )
76+ return process .wait ()
77+
78+
4979def launch_shell (
5080 host : str ,
5181 context : str ,
@@ -54,7 +84,7 @@ def launch_shell(
5484 use_profiles : bool ,
5585 * ,
5686 command : tuple [str , ...] | None = None ,
57- process_callback = None ,
87+ lease = None ,
5888) -> int :
5989 """Launch a shell with a custom prompt indicating the exporter type.
6090
@@ -63,7 +93,12 @@ def launch_shell(
6393 context: The context of the shell (e.g. "local" or exporter name)
6494 allow: List of allowed drivers
6595 unsafe: Whether to allow drivers outside of the allow list
66- process_callback: Optional callback to receive the process object before waiting
96+ use_profiles: Whether to load shell profile files
97+ command: Optional command to run instead of launching an interactive shell
98+ lease: Optional Lease object to set up lease ending callback
99+
100+ Returns:
101+ The exit code of the shell or command process
67102 """
68103
69104 shell = os .environ .get ("SHELL" , "bash" )
@@ -75,23 +110,16 @@ def launch_shell(
75110 }
76111
77112 if command :
78- process = Popen (command , stdin = sys .stdin , stdout = sys .stdout , stderr = sys .stderr , env = common_env )
79- if process_callback :
80- process_callback (process )
81- return process .wait ()
113+ return _run_process (list (command ), common_env , lease )
82114
83115 if shell_name .endswith ("bash" ):
84116 env = common_env | {
85117 "PS1" : f"{ ANSI_GRAY } { PROMPT_CWD } { ANSI_YELLOW } ⚡{ ANSI_WHITE } { context } { ANSI_YELLOW } ➤{ ANSI_RESET } " ,
86118 }
87-
88119 cmd = [shell ]
89120 if not use_profiles :
90121 cmd .extend (["--norc" , "--noprofile" ])
91- process = Popen (cmd , stdin = sys .stdin , stdout = sys .stdout , stderr = sys .stderr , env = env )
92- if process_callback :
93- process_callback (process )
94- return process .wait ()
122+ return _run_process (cmd , env , lease )
95123
96124 elif shell_name == "fish" :
97125 fish_fn = (
@@ -108,32 +136,20 @@ def launch_shell(
108136 "end"
109137 )
110138 cmd = [shell , "--init-command" , fish_fn ]
111- process = Popen (cmd , stdin = sys .stdin , stdout = sys .stdout , stderr = sys .stderr , env = common_env )
112- if process_callback :
113- process_callback (process )
114- return process .wait ()
139+ return _run_process (cmd , common_env , lease )
115140
116141 elif shell_name == "zsh" :
117142 env = common_env | {
118143 "PS1" : f"%F{{8}}%1~ %F{{yellow}}⚡%F{{white}}{ context } %F{{yellow}}➤%f " ,
119144 }
120-
121145 if "HISTFILE" not in env :
122146 env ["HISTFILE" ] = os .path .join (os .path .expanduser ("~" ), ".zsh_history" )
123147
124148 cmd = [shell ]
125149 if not use_profiles :
126150 cmd .append ("--no-rcs" )
127-
128151 cmd .extend (["-o" , "inc_append_history" , "-o" , "share_history" ])
129-
130- process = Popen (cmd , stdin = sys .stdin , stdout = sys .stdout , stderr = sys .stderr , env = env )
131- if process_callback :
132- process_callback (process )
133- return process .wait ()
152+ return _run_process (cmd , env , lease )
134153
135154 else :
136- process = Popen ([shell ], stdin = sys .stdin , stdout = sys .stdout , stderr = sys .stderr , env = common_env )
137- if process_callback :
138- process_callback (process )
139- return process .wait ()
155+ return _run_process ([shell ], common_env , lease )
0 commit comments