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,34 @@ def serve(root_device: Driver):
4649PROMPT_CWD = "\\ W"
4750
4851
52+ def lease_ending_handler (process : Popen , lease , remaining_time ) -> None :
53+ """Lease ending handler to terminate a process when lease ends.
54+
55+ Args:
56+ process: The process to terminate
57+ lease: The lease instance
58+ remaining_time: Time remaining until lease expiration
59+ """
60+
61+ if remaining_time <= timedelta (0 ):
62+ try :
63+ process .send_signal (signal .SIGHUP )
64+ except (ProcessLookupError , OSError ):
65+ pass # Process already terminated
66+
67+
68+ def _run_process (
69+ cmd : list [str ],
70+ env : dict [str , str ],
71+ lease = None ,
72+ ) -> int :
73+ """Helper to run a process with an option to set a lease ending callback."""
74+ process = Popen (cmd , stdin = sys .stdin , stdout = sys .stdout , stderr = sys .stderr , env = env )
75+ if lease is not None :
76+ lease .lease_ending_callback = partial (lease_ending_handler , process )
77+ return process .wait ()
78+
79+
4980def launch_shell (
5081 host : str ,
5182 context : str ,
@@ -54,6 +85,7 @@ def launch_shell(
5485 use_profiles : bool ,
5586 * ,
5687 command : tuple [str , ...] | None = None ,
88+ lease = None ,
5789) -> int :
5890 """Launch a shell with a custom prompt indicating the exporter type.
5991
@@ -62,6 +94,12 @@ def launch_shell(
6294 context: The context of the shell (e.g. "local" or exporter name)
6395 allow: List of allowed drivers
6496 unsafe: Whether to allow drivers outside of the allow list
97+ use_profiles: Whether to load shell profile files
98+ command: Optional command to run instead of launching an interactive shell
99+ lease: Optional Lease object to set up lease ending callback
100+
101+ Returns:
102+ The exit code of the shell or command process
65103 """
66104
67105 shell = os .environ .get ("SHELL" , "bash" )
@@ -73,19 +111,16 @@ def launch_shell(
73111 }
74112
75113 if command :
76- process = Popen (command , stdin = sys .stdin , stdout = sys .stdout , stderr = sys .stderr , env = common_env )
77- return process .wait ()
114+ return _run_process (list (command ), common_env , lease )
78115
79116 if shell_name .endswith ("bash" ):
80117 env = common_env | {
81118 "PS1" : f"{ ANSI_GRAY } { PROMPT_CWD } { ANSI_YELLOW } ⚡{ ANSI_WHITE } { context } { ANSI_YELLOW } ➤{ ANSI_RESET } " ,
82119 }
83-
84120 cmd = [shell ]
85121 if not use_profiles :
86122 cmd .extend (["--norc" , "--noprofile" ])
87- process = Popen (cmd , stdin = sys .stdin , stdout = sys .stdout , stderr = sys .stderr , env = env )
88- return process .wait ()
123+ return _run_process (cmd , env , lease )
89124
90125 elif shell_name == "fish" :
91126 fish_fn = (
@@ -102,26 +137,20 @@ def launch_shell(
102137 "end"
103138 )
104139 cmd = [shell , "--init-command" , fish_fn ]
105- process = Popen (cmd , stdin = sys .stdin , stdout = sys .stdout , stderr = sys .stderr , env = common_env )
106- return process .wait ()
140+ return _run_process (cmd , common_env , lease )
107141
108142 elif shell_name == "zsh" :
109143 env = common_env | {
110144 "PS1" : f"%F{{8}}%1~ %F{{yellow}}⚡%F{{white}}{ context } %F{{yellow}}➤%f " ,
111145 }
112-
113146 if "HISTFILE" not in env :
114147 env ["HISTFILE" ] = os .path .join (os .path .expanduser ("~" ), ".zsh_history" )
115148
116149 cmd = [shell ]
117150 if not use_profiles :
118151 cmd .append ("--no-rcs" )
119-
120152 cmd .extend (["-o" , "inc_append_history" , "-o" , "share_history" ])
121-
122- process = Popen (cmd , stdin = sys .stdin , stdout = sys .stdout , stderr = sys .stderr , env = env )
123- return process .wait ()
153+ return _run_process (cmd , env , lease )
124154
125155 else :
126- process = Popen ([shell ], stdin = sys .stdin , stdout = sys .stdout , stderr = sys .stderr , env = common_env )
127- return process .wait ()
156+ return _run_process ([shell ], common_env , lease )
0 commit comments