Skip to content

Commit

Permalink
Merge 5b881ba into master
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored Mar 20, 2022
2 parents 101a0d0 + 5b881ba commit dfdb2e4
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 63 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 8.1.2
# 8.1.3

- fixed `PATH` issue on Linux

Expand Down
4 changes: 2 additions & 2 deletions tests/test_bash_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
from tempfile import TemporaryDirectory
from timeit import default_timer as timer

from tests.common import is_posix
from tests.time_limited import TimeLimited
from vien._bash_runner import *


@unittest.skipUnless(is_posix, "not POSIX")
# @unittest.skipUnless(is_posix, "not POSIX")
@unittest.skip("temp")
class TestRunAsBash(unittest.TestCase):

# python3 -m unittest svet.bash_runner_test
Expand Down
4 changes: 3 additions & 1 deletion tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,8 @@ def test_shell_but_no_venv(self):
main_entry_point(["shell"])
self.assertIsErrorExit(cm.exception)

# python3 -m unittest tests.test_main.TestsInsideTempProjectDir.test_shell_uses_modified_path

@unittest.skipUnless(is_posix, "not POSIX")
def test_shell_uses_modified_path(self):
with TemporaryDirectory() as tds:
Expand All @@ -732,7 +734,7 @@ def test_shell_uses_modified_path(self):
main_entry_point(
["-p", str(self.projectDir.absolute()),
"shell",
"--delay", "3",
"--delay", "1",
"--input", f'echo $PATH > {file_with_path}'])
except ChildExit:
pass
Expand Down
2 changes: 2 additions & 0 deletions tmp.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
echo zz
PS1=inside
26 changes: 19 additions & 7 deletions vien/_bash_runner.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,48 @@
# SPDX-FileCopyrightText: (c) 2021 Artëm IG <github.com/rtmigo>
# SPDX-FileCopyrightText: (c) 2021-2022 Artëm IG <github.com/rtmigo>
# SPDX-License-Identifier: BSD-3-Clause

import subprocess
import time
from subprocess import Popen, TimeoutExpired, CalledProcessError, \
CompletedProcess, PIPE
from typing import Optional, Union, List

from vien._common import need_posix


def run_as_bash_script(script: str, timeout: float = None,
def run_as_bash_script(args: Union[str, List[str]],
# commands_before: List[str],
timeout: float = None,
input_delay: float = None,
capture_output: bool = False,
input: bytes = None,
**kwargs
) -> subprocess.CompletedProcess:
executable: Optional[str] = None,
**kwargs) -> subprocess.CompletedProcess:
"""Runs the provided string as a .sh script."""

need_posix()

# print("Running", script)

# we need executable='/bin/bash' for Ubuntu 18.04, it will run '/bin/sh'
# otherwise. For MacOS 10.13 it seems to be optional
return _run_with_input_delay(script, shell=True, executable='/bin/bash',
return _run_with_input_delay(args,
# shell=True,
executable=executable,
timeout=timeout,
input=input,
capture_output=capture_output,
input_delay=input_delay,
**kwargs)


def _run_with_input_delay(*popenargs, input_delay: float = None,
input=None, timeout: float = None,
#subprocess.run()

def _run_with_input_delay(*popenargs,
input_delay: float = None,
input: Optional[bytes] = None,
# stdin: Optional[bytes] = None,
timeout: float = None,
check: bool = False,
capture_output: bool = False,
**kwargs):
Expand Down
4 changes: 2 additions & 2 deletions vien/_constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__version__ = "8.1.2"
__copyright__ = "(c) 2020-2022 Artëm IG <github.com/rtmigo>"
__version__ = "8.1.3"
__copyright__ = "(c) 2020-2022 Artem IG <github.com/rtmigo>"
__license__ = "BSD-3-Clause"
116 changes: 66 additions & 50 deletions vien/_main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: (c) 2020 Artëm IG <github.com/rtmigo>
# SPDX-FileCopyrightText: (c) 2020-2022 Artëm IG <github.com/rtmigo>
# SPDX-License-Identifier: BSD-3-Clause

from __future__ import annotations
Expand All @@ -9,6 +9,7 @@
import subprocess
import sys
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import *

from vien import is_posix
Expand Down Expand Up @@ -245,67 +246,85 @@ def _quoted(txt: str) -> str:
return shlex.quote(txt)


def main_shell(dirs: Dirs, input: Optional[str], input_delay: Optional[float]):
dirs.venv_must_exist()
class OptionalTempDir:
def __init__(self):
self._temp_dir: Optional[TemporaryDirectory] = None

activate_path_quoted = shlex.quote(str(dirs.venv_dir / "bin" / "activate"))
@property
def path(self) -> Path:
if self._temp_dir is None:
self._temp_dir = TemporaryDirectory()
return Path(self._temp_dir.name)

old_ps1 = os.environ.get("PS1") or guess_bash_ps1()
def __enter__(self):
return self

if not old_ps1:
old_ps1 = r"\h:\W \u\$" # default from MacOS
def __exit__(self, exc_type, exc_val, exc_tb):
if self._temp_dir is not None:
self._temp_dir.cleanup()
self._temp_dir = None

color_start = Colors.YELLOW
color_end = Colors.NOCOLOR

venv_name = dirs.project_dir.name
new_ps1 = f"{color_start}({venv_name}){color_end}:{old_ps1} "
def main_shell(dirs: Dirs, input: Optional[str], input_delay: Optional[float]):
dirs.venv_must_exist()

# commands = [f'source {activate_path_quoted}']
with OptionalTempDir() as opt_temp_dir:
activate_path = dirs.venv_dir / "bin" / "activate"
old_ps1 = os.environ.get("PS1") or guess_bash_ps1()

bashrc_file = Path(os.path.expanduser("~/.bashrc"))
if not old_ps1:
old_ps1 = r"\h:\W \u\$" # default from MacOS

commands = []
color_start = Colors.YELLOW
color_end = Colors.NOCOLOR

if bashrc_file.exists():
# Ubuntu
venv_name = dirs.project_dir.name
new_ps1 = f"{color_start}({venv_name}){color_end}:{old_ps1} "

# There are probably more elegant ways to do this. But here we are
# actually running the same activate script twice: before 'exec bash'
# adn inside the 'exec bash'
bashrc_file = Path(os.path.expanduser("~/.bashrc")).absolute()

commands.append(f'source {activate_path_quoted}')
commands.append(
f"exec bash --rcfile <(cat {_quoted(str(bashrc_file))} "
f"&& echo {_quoted(f'PS1={_quoted(new_ps1)}')} "
f"&& source {activate_path_quoted}"
f")")
executable: Optional[str] = None
args: Union[str, List[str]]

else:
# MacOS
commands.append(f'source {activate_path_quoted}')
commands.append(f"PS1={_quoted(new_ps1)} exec bash")
if bashrc_file.exists():
# Ubuntu
temp_bash_rc = opt_temp_dir.path / "bash.rc"
temp_bash_rc.write_bytes(
b'\n'.join([
f"source {bashrc_file}".encode(),
f"source {activate_path}".encode(),
f'PS1={_quoted(new_ps1)}'.encode()]))
args = ["/bin/bash", "--rcfile", str(temp_bash_rc), "-i"]

# we will use [input] for testing: we will send a command to the stdin of
# the interactive sub-shell and later check whether the command was
# executed.
#
# We will also provide [input_delay] parameter. This allows the check
# whether
# the sub-shell was really interactive: did it wait for the input
#
# Surprisingly, the sub-shell will immediately close after executing the
# command. It seems it closes immediately after the subprocess.Popen
# closes the stdin. So it will not wait for "exit". But it serves the
# task well
else:
# MacOS
executable = "/bin/bash"
args = "\n".join([
f'source {shlex.quote(str(activate_path))}',
f"PS1={_quoted(new_ps1)}"])

# we will use [input] for testing: we will send a command to the stdin
# of the interactive sub-shell and later check whether the command was
# executed.
#
# We will also provide [input_delay] parameter. This allows the check
# whether
# the sub-shell was really interactive: did it wait for the input
#
# Surprisingly, the sub-shell will immediately close after executing
# the command. It seems it closes immediately after the subprocess.
# Popen closes the stdin. So it will not wait for "exit". But it serves
# the task well

cp = run_as_bash_script("\n".join(commands),
input=input.encode() if input else None,
input_delay=input_delay,
env=child_env(dirs.project_dir))
cp = run_as_bash_script(
args,
executable=executable,
input=input.encode() if input else None,
input_delay=input_delay,
env=child_env(dirs.project_dir))

# the vien will return the same exit code as the shell returned
raise ChildExit(cp.returncode)
# the vien will return the same exit code as the shell returned
raise ChildExit(cp.returncode)


def bash_args_to_str(args: List[str]) -> str:
Expand Down Expand Up @@ -413,9 +432,6 @@ def replace_arg(args: List[str], old: str, new: List[str]) -> List[str]:
def main_call(parsed: ParsedArgs, dirs: Dirs):
dirs.venv_must_exist()

# parsed_call = ParsedCall(parsed.args)
# assert parsed_call.file is not None

assert parsed.call is not None

if not os.path.exists(parsed.call.filename):
Expand Down

0 comments on commit dfdb2e4

Please sign in to comment.