Skip to content

Commit cfa1557

Browse files
Merge 56137b8 into master
2 parents dfdb2e4 + 56137b8 commit cfa1557

File tree

4 files changed

+70
-167
lines changed

4 files changed

+70
-167
lines changed

tests/test_bash_runner.py

Lines changed: 0 additions & 59 deletions
This file was deleted.

vien/_bash_runner.py

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,19 @@
11
# SPDX-FileCopyrightText: (c) 2021-2022 Artëm IG <github.com/rtmigo>
22
# SPDX-License-Identifier: BSD-3-Clause
3+
from __future__ import annotations
34

4-
import subprocess
5+
import os
56
import time
7+
from pathlib import Path
68
from subprocess import Popen, TimeoutExpired, CalledProcessError, \
79
CompletedProcess, PIPE
8-
from typing import Optional, Union, List
10+
from tempfile import NamedTemporaryFile
11+
from typing import Optional, List, Dict
912

10-
from vien._common import need_posix
11-
12-
13-
def run_as_bash_script(args: Union[str, List[str]],
14-
# commands_before: List[str],
15-
timeout: float = None,
16-
input_delay: float = None,
17-
capture_output: bool = False,
18-
input: bytes = None,
19-
executable: Optional[str] = None,
20-
**kwargs) -> subprocess.CompletedProcess:
21-
"""Runs the provided string as a .sh script."""
22-
23-
need_posix()
24-
25-
# print("Running", script)
26-
27-
# we need executable='/bin/bash' for Ubuntu 18.04, it will run '/bin/sh'
28-
# otherwise. For MacOS 10.13 it seems to be optional
29-
return _run_with_input_delay(args,
30-
# shell=True,
31-
executable=executable,
32-
timeout=timeout,
33-
input=input,
34-
capture_output=capture_output,
35-
input_delay=input_delay,
36-
**kwargs)
37-
38-
39-
#subprocess.run()
4013

4114
def _run_with_input_delay(*popenargs,
4215
input_delay: float = None,
4316
input: Optional[bytes] = None,
44-
# stdin: Optional[bytes] = None,
4517
timeout: float = None,
4618
check: bool = False,
4719
capture_output: bool = False,
@@ -113,3 +85,34 @@ def _run_with_input_delay(*popenargs,
11385
output=stdout, stderr=stderr)
11486

11587
return CompletedProcess(process.args, exit_code, stdout, stderr)
88+
89+
90+
def start_bash_shell(init_commands: List[str],
91+
input: Optional[str] = None,
92+
input_delay: Optional[float] = None,
93+
env: Optional[Dict] = None) -> CompletedProcess:
94+
ubuntu_bashrc_path = Path(os.path.expanduser("~/.bashrc")).absolute()
95+
96+
if ubuntu_bashrc_path.exists():
97+
# Ubuntu
98+
with NamedTemporaryFile('r', suffix=".rc") as ntf:
99+
init_commands = [f"source {ubuntu_bashrc_path}"] + init_commands
100+
101+
# creating temporary init script (like bash.rc)
102+
temp_bash_rc = Path(ntf.name)
103+
temp_bash_rc.write_text('\n'.join(init_commands))
104+
105+
return _run_with_input_delay(
106+
["/bin/bash", "--rcfile", str(temp_bash_rc), "-i"],
107+
executable=None,
108+
input=input.encode() if input else None,
109+
input_delay=input_delay,
110+
env=env)
111+
else:
112+
# MacOS
113+
return _run_with_input_delay(
114+
"\n".join(init_commands),
115+
executable="/bin/bash",
116+
input=input.encode() if input else None,
117+
input_delay=input_delay,
118+
env=env)

vien/_constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
__version__ = "8.1.3"
1+
__version__ = "8.1.4"
22
__copyright__ = "(c) 2020-2022 Artem IG <github.com/rtmigo>"
33
__license__ = "BSD-3-Clause"

vien/_main.py

Lines changed: 33 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@
99
import subprocess
1010
import sys
1111
from pathlib import Path
12-
from tempfile import TemporaryDirectory
1312
from typing import *
1413

1514
from vien import is_posix
16-
from vien._bash_runner import run_as_bash_script
15+
from vien._bash_runner import start_bash_shell
1716
from vien._call_funcs import relative_fn_to_module_name, relative_inner_path
1817
from vien._cmdexe_escape_args import cmd_escape_arg
1918
from vien._colors import Colors
@@ -246,85 +245,45 @@ def _quoted(txt: str) -> str:
246245
return shlex.quote(txt)
247246

248247

249-
class OptionalTempDir:
250-
def __init__(self):
251-
self._temp_dir: Optional[TemporaryDirectory] = None
252-
253-
@property
254-
def path(self) -> Path:
255-
if self._temp_dir is None:
256-
self._temp_dir = TemporaryDirectory()
257-
return Path(self._temp_dir.name)
258-
259-
def __enter__(self):
260-
return self
261-
262-
def __exit__(self, exc_type, exc_val, exc_tb):
263-
if self._temp_dir is not None:
264-
self._temp_dir.cleanup()
265-
self._temp_dir = None
266-
267-
268248
def main_shell(dirs: Dirs, input: Optional[str], input_delay: Optional[float]):
269249
dirs.venv_must_exist()
270250

271-
with OptionalTempDir() as opt_temp_dir:
272-
activate_path = dirs.venv_dir / "bin" / "activate"
273-
old_ps1 = os.environ.get("PS1") or guess_bash_ps1()
274-
275-
if not old_ps1:
276-
old_ps1 = r"\h:\W \u\$" # default from MacOS
277-
278-
color_start = Colors.YELLOW
279-
color_end = Colors.NOCOLOR
251+
# with OptionalTempDir() as opt_temp_dir:
252+
activate_path = dirs.venv_dir / "bin" / "activate"
253+
old_ps1 = os.environ.get("PS1") or guess_bash_ps1()
280254

281-
venv_name = dirs.project_dir.name
282-
new_ps1 = f"{color_start}({venv_name}){color_end}:{old_ps1} "
255+
if not old_ps1:
256+
old_ps1 = r"\h:\W \u\$" # default from MacOS
283257

284-
bashrc_file = Path(os.path.expanduser("~/.bashrc")).absolute()
258+
color_start = Colors.YELLOW
259+
color_end = Colors.NOCOLOR
285260

286-
executable: Optional[str] = None
287-
args: Union[str, List[str]]
261+
venv_name = dirs.project_dir.name
262+
new_ps1 = f"{color_start}({venv_name}){color_end}:{old_ps1} "
288263

289-
if bashrc_file.exists():
290-
# Ubuntu
291-
temp_bash_rc = opt_temp_dir.path / "bash.rc"
292-
temp_bash_rc.write_bytes(
293-
b'\n'.join([
294-
f"source {bashrc_file}".encode(),
295-
f"source {activate_path}".encode(),
296-
f'PS1={_quoted(new_ps1)}'.encode()]))
297-
args = ["/bin/bash", "--rcfile", str(temp_bash_rc), "-i"]
298-
299-
else:
300-
# MacOS
301-
executable = "/bin/bash"
302-
args = "\n".join([
303-
f'source {shlex.quote(str(activate_path))}',
304-
f"PS1={_quoted(new_ps1)}"])
305-
306-
# we will use [input] for testing: we will send a command to the stdin
307-
# of the interactive sub-shell and later check whether the command was
308-
# executed.
309-
#
310-
# We will also provide [input_delay] parameter. This allows the check
311-
# whether
312-
# the sub-shell was really interactive: did it wait for the input
313-
#
314-
# Surprisingly, the sub-shell will immediately close after executing
315-
# the command. It seems it closes immediately after the subprocess.
316-
# Popen closes the stdin. So it will not wait for "exit". But it serves
317-
# the task well
318-
319-
cp = run_as_bash_script(
320-
args,
321-
executable=executable,
322-
input=input.encode() if input else None,
323-
input_delay=input_delay,
324-
env=child_env(dirs.project_dir))
325-
326-
# the vien will return the same exit code as the shell returned
327-
raise ChildExit(cp.returncode)
264+
# we use [input] for testing: we send a command to the stdin of the
265+
# interactive sub-shell and later check whether the command was
266+
# executed.
267+
#
268+
# We will also provide [input_delay] parameter. This allows the check
269+
# whether the sub-shell was really interactive: did it wait for
270+
# the input
271+
#
272+
# Surprisingly, the sub-shell will immediately close after executing
273+
# the command. It seems it closes immediately after the subprocess.
274+
# Popen closes the stdin. So it will not wait for "exit". But it serves
275+
# the task well
276+
277+
cp = start_bash_shell(init_commands=[
278+
f'source {shlex.quote(str(activate_path))}',
279+
f"PS1={_quoted(new_ps1)}"],
280+
input=input,
281+
input_delay=input_delay,
282+
env=child_env(dirs.project_dir)
283+
)
284+
285+
# the vien will return the same exit code as the shell returned
286+
raise ChildExit(cp.returncode)
328287

329288

330289
def bash_args_to_str(args: List[str]) -> str:

0 commit comments

Comments
 (0)