From 523e6e369d027b3783b5fda986b985b7d93293de Mon Sep 17 00:00:00 2001 From: rtmigo Date: Wed, 26 May 2021 09:01:15 +0300 Subject: [PATCH 01/11] better escape --- vien/main.py | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/vien/main.py b/vien/main.py index 64f3eb8..99478fd 100755 --- a/vien/main.py +++ b/vien/main.py @@ -87,7 +87,7 @@ def run_cmdexe_sequence(commands: List[str], env: Optional[Dict] = None) -> int: glued = " && ".join(f'( {c} )' for c in commands) - #print(f"CMD running {glued}") + # print(f"CMD running {glued}") return subprocess.call(glued, shell=True, @@ -95,12 +95,12 @@ def run_cmdexe_sequence(commands: List[str], env: Optional[Dict] = None) -> int: env=env) -def quote_shell_arg(arg: str) -> str: - # todo two separate functions called on need - if is_posix: - return shlex.quote(arg) - else: - return cmd_escape_arg(arg) +# def quote_shell_arg(arg: str) -> str: +# # todo two separate functions called on need +# if is_posix: +# return shlex.quote(arg) +# else: +# return cmd_escape_arg(arg) def venv_dir_to_python_exe(venv_dir: Path) -> Path: @@ -243,7 +243,7 @@ def guess_bash_ps1(): def main_shell(dirs: Dirs, input: Optional[str], input_delay: Optional[float]): dirs.venv_must_exist() - activate_path_quoted = quote_shell_arg( + activate_path_quoted = shlex.quote( str(dirs.venv_dir / "bin" / "activate")) old_ps1 = os.environ.get("PS1") or guess_bash_ps1() @@ -294,19 +294,28 @@ def main_shell(dirs: Dirs, input: Optional[str], input_delay: Optional[float]): # def _run(dirs: Dirs, other_args: List[str]): +def bash_args_to_str(args: List[str]) -> str: + return ' '.join(shlex.quote(arg) for arg in args) + + +def cmdexe_args_to_str(args: List[str]) -> str: + return ' '.join(cmd_escape_arg(arg) for arg in args) + -def main_run(dirs: Dirs, other_args: List[str]): +def main_run(dirs: Dirs, command: List[str]): dirs.venv_must_exist() - commands: List[str] = list() + sequence: List[str] = list() if is_posix: activate_file = posix_bash_activate(dirs.venv_dir) - commands.append(f'source "{activate_file}"') + sequence.append(f'source {shlex.quote(str(activate_file))}') + sequence.append(bash_args_to_str(command)) run_func = run_bash_sequence elif is_windows: activate_file = windows_cmdexe_activate(dirs.venv_dir) - commands.append(f'CALL "{activate_file}"') + sequence.append(f'CALL {activate_file}"') + sequence.append(cmdexe_args_to_str(command)) run_func = run_cmdexe_sequence else: raise AssertionError("Unexpected OS") @@ -316,9 +325,9 @@ def main_run(dirs: Dirs, other_args: List[str]): # if prepend_py_path: # commands.append(f'export PYTHONPATH="{prepend_py_path}:$PYTHONPATH"') - commands.append(" ".join(quote_shell_arg(a) for a in other_args)) + # sequence.append(" ".join(quote_shell_arg(a) for a in command)) - exit_code = run_func(commands, env=child_env(dirs.project_dir)) + exit_code = run_func(sequence, env=child_env(dirs.project_dir)) raise ChildExit(exit_code) From d68a4332b78f2664da29123ab8e26b80c695d77c Mon Sep 17 00:00:00 2001 From: rtmigo Date: Wed, 26 May 2021 18:02:46 +0300 Subject: [PATCH 02/11] enablisg windows --- tests/main_test.py | 26 +++++++++++++++++++++----- vien/arg_parser.py | 43 +++++++++++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/tests/main_test.py b/tests/main_test.py index 7ffba4f..0e72b46 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -13,6 +13,11 @@ from pathlib import Path from tempfile import TemporaryDirectory from timeit import default_timer as timer +from typing import List + +from vien.arg_parser import Parsed + +from vien._common import is_windows from tests.common import is_posix from tests.time_limited import TimeLimited @@ -66,6 +71,11 @@ def test_path(self): main_entry_point(["path"]) +def windows_too(args: List[str]) -> List[str]: + if is_windows: + return [Parsed.PARAM_WINDOWS_ALL_ARGS] + args + + class TestsInsideTempProjectDir(unittest.TestCase): def setUp(self): @@ -266,9 +276,15 @@ def test_recreate_resolves_python3(self): ############################################################################ + @unittest.skipUnless(is_windows, "testing windows limitations") + def test_no_run_in_windows(self): + with self.assertRaises(SystemExit) as cm: + main_entry_point(["run", "python", "-c", "pass"]) + self.assertEqual(cm.exception.code, 2) + def test_run_needs_venv(self): with self.assertRaises(VenvDoesNotExistExit) as cm: - main_entry_point(["run", "python", "-c", "pass"]) + main_entry_point(windows_too(["run", "python", "-c", "pass"])) self.assertIsErrorExit(cm.exception) def test_run_exit_code_0(self): @@ -276,7 +292,7 @@ def test_run_exit_code_0(self): as the called command""" main_entry_point(["create"]) # need venv to run with self.assertRaises(ChildExit) as ce: - main_entry_point(["run", "python3", "-c", "exit(0)"]) + main_entry_point(windows_too(["run", "python3", "-c", "exit(0)"])) self.assertEqual(ce.exception.code, 0) def test_run_exit_code_1(self): @@ -284,7 +300,7 @@ def test_run_exit_code_1(self): as the called command""" main_entry_point(["create"]) # need venv to run with self.assertRaises(ChildExit) as ce: - main_entry_point(["run", "python3", "-c", "exit(1)"]) + main_entry_point(windows_too(["run", "python3", "-c", "exit(1)"])) self.assertEqual(ce.exception.code, 1) def test_run_exit_code_2(self): @@ -292,7 +308,7 @@ def test_run_exit_code_2(self): as the called command""" main_entry_point(["create"]) # need venv to run with self.assertRaises(ChildExit) as ce: - main_entry_point(["run", "python3", "-c", "exit(2)"]) + main_entry_point(windows_too(["run", "python3", "-c", "exit(2)"])) self.assertEqual(ce.exception.code, 2) def test_run_python_version(self): @@ -301,7 +317,7 @@ def test_run_python_version(self): with self.assertRaises(ChildExit): # just check the argparser handles --version properly # (was failing with nargs='*', ok with nargs=argparse.REMAINDER) - main_entry_point(["run", "python3", "--version"]) + main_entry_point(windows_too(["run", "python3", "--version"])) @unittest.skipUnless(is_posix, "not POSIX") def test_run_p(self): diff --git a/vien/arg_parser.py b/vien/arg_parser.py index 3b5a8a9..250fd44 100644 --- a/vien/arg_parser.py +++ b/vien/arg_parser.py @@ -3,6 +3,8 @@ from enum import Enum from typing import List, Optional +from vien import is_posix + import vien from vien.call_parser import items_after @@ -47,13 +49,25 @@ class Commands(Enum): class Parsed: + PARAM_WINDOWS_ALL_ARGS = "--windows-all-args" + def __init__(self, args: Optional[List[str]]): super().__init__() + if args is None: + args = sys.argv[1:] + + enable_windows_all_args = self.PARAM_WINDOWS_ALL_ARGS in args + parser = argparse.ArgumentParser() parser.add_argument("--project-dir", "-p", default=None, type=str) + # the following parameter is added only to avoid parsing errors. + # Actually we use its value from `args` before running ArgumentParser + parser.add_argument(self.PARAM_WINDOWS_ALL_ARGS, action='store_true', + help=argparse.SUPPRESS) + subparsers = parser.add_subparsers(dest='command', required=True) parser_init = subparsers.add_parser(Commands.create.name, @@ -70,17 +84,19 @@ def __init__(self, args: Optional[List[str]]): parser_reinit.add_argument('python', type=str, default=None, nargs='?') - shell_parser = subparsers.add_parser( - Commands.shell.name, - help="dive into Bash sub-shell using the virtualenv") - shell_parser.add_argument("--input", type=str, default=None) - shell_parser.add_argument("--delay", type=float, default=None, - help=argparse.SUPPRESS) + if is_posix or enable_windows_all_args: + shell_parser = subparsers.add_parser( + Commands.shell.name, + help="dive into Bash sub-shell using the virtualenv") + shell_parser.add_argument("--input", type=str, default=None) + shell_parser.add_argument("--delay", type=float, default=None, + help=argparse.SUPPRESS) - parser_run = subparsers.add_parser( - Commands.run.name, - help="run a command inside the virtualenv") - parser_run.add_argument('otherargs', nargs=argparse.REMAINDER) + if is_posix or enable_windows_all_args: + parser_run = subparsers.add_parser( + Commands.run.name, + help="run a command inside the virtualenv") + parser_run.add_argument('otherargs', nargs=argparse.REMAINDER) parser_call = subparsers.add_parser( Commands.call.name, @@ -98,9 +114,6 @@ def __init__(self, args: Optional[List[str]]): help="show the supposed path of the virtualenv " "for the current directory") - if args is None: - args = sys.argv[1:] - if not args: print(usage_doc()) parser.print_help() @@ -164,7 +177,7 @@ def project_dir_arg(self) -> Optional[str]: def python_executable(self) -> Optional[str]: if self.command not in (Commands.create, Commands.recreate): raise RuntimeError - #assert self._ns.python is not None + # assert self._ns.python is not None return self._ns.python @property @@ -184,5 +197,3 @@ def run_args(self) -> List[str]: if self.command != Commands.run: raise RuntimeError return self._ns.otherargs - - From ca5bf98ec0e1aa9b05dbe269d8470f477b81e171 Mon Sep 17 00:00:00 2001 From: rtmigo Date: Wed, 26 May 2021 18:09:02 +0300 Subject: [PATCH 03/11] enabling windows --- tests/main_test.py | 2 ++ vien/arg_parser.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/tests/main_test.py b/tests/main_test.py index 0e72b46..67d6bed 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -74,6 +74,8 @@ def test_path(self): def windows_too(args: List[str]) -> List[str]: if is_windows: return [Parsed.PARAM_WINDOWS_ALL_ARGS] + args + else: + return args class TestsInsideTempProjectDir(unittest.TestCase): diff --git a/vien/arg_parser.py b/vien/arg_parser.py index 250fd44..6760116 100644 --- a/vien/arg_parser.py +++ b/vien/arg_parser.py @@ -3,6 +3,8 @@ from enum import Enum from typing import List, Optional +from vien._common import is_windows + from vien import is_posix import vien @@ -58,6 +60,10 @@ def __init__(self, args: Optional[List[str]]): args = sys.argv[1:] enable_windows_all_args = self.PARAM_WINDOWS_ALL_ARGS in args + if enable_windows_all_args: + # for more transparent testing, I don't want this param + # to ever affect posix behavior + assert is_windows parser = argparse.ArgumentParser() From 0830880622778d65335766592b13e7c8be58c308 Mon Sep 17 00:00:00 2001 From: rtmigo Date: Wed, 26 May 2021 18:14:44 +0300 Subject: [PATCH 04/11] enabling windows --- tests/main_test.py | 7 ++----- tests/test_arg_parser.py | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/main_test.py b/tests/main_test.py index 67d6bed..c33140a 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -15,6 +15,7 @@ from timeit import default_timer as timer from typing import List +from tests.test_arg_parser import windows_too from vien.arg_parser import Parsed from vien._common import is_windows @@ -71,11 +72,7 @@ def test_path(self): main_entry_point(["path"]) -def windows_too(args: List[str]) -> List[str]: - if is_windows: - return [Parsed.PARAM_WINDOWS_ALL_ARGS] + args - else: - return args + class TestsInsideTempProjectDir(unittest.TestCase): diff --git a/tests/test_arg_parser.py b/tests/test_arg_parser.py index 84b7e9a..cb01f5e 100644 --- a/tests/test_arg_parser.py +++ b/tests/test_arg_parser.py @@ -1,10 +1,18 @@ import unittest from pathlib import Path +from typing import List + +from vien._common import is_windows from tests.common import is_posix from vien.main import get_project_dir from vien.arg_parser import Parsed, Commands +def windows_too(args: List[str]) -> List[str]: + if is_windows: + return [Parsed.PARAM_WINDOWS_ALL_ARGS] + args + else: + return args class TestProjectDir(unittest.TestCase): @@ -56,28 +64,28 @@ def test_unrecoginzed(self): class TestParseShell(unittest.TestCase): def test_no_args(self): - pd = Parsed('shell'.split()) + pd = Parsed(windows_too('shell'.split())) self.assertEqual(pd.command, Commands.shell) self.assertEqual(pd.shell_delay, None) self.assertEqual(pd.shell_input, None) def test_input(self): - pd = Parsed(['shell', '--input', 'cd / && ls']) + pd = Parsed(windows_too(['shell', '--input', 'cd / && ls'])) self.assertEqual(pd.shell_input, 'cd / && ls') def test_delay(self): - pd = Parsed('shell --delay 1.2'.split()) + pd = Parsed(windows_too('shell --delay 1.2'.split())) self.assertEqual(pd.shell_delay, 1.2) def test_labuda(self): with self.assertRaises(SystemExit) as ce: - pd = Parsed('shell --labuda'.split()) + pd = Parsed(windows_too('shell --labuda'.split())) self.assertEqual(ce.exception.code, 2) class TestParseRun(unittest.TestCase): def test(self): - pd = Parsed(['run', 'python3', '-OO', 'file.py']) + pd = Parsed(windows_too(['run', 'python3', '-OO', 'file.py'])) self.assertEqual(pd.command, Commands.run) self.assertEqual(pd.run_args, ['python3', '-OO', 'file.py']) From b1b36ec2e1a64ea27137d39c8fdfb1296f9783af Mon Sep 17 00:00:00 2001 From: rtmigo Date: Wed, 26 May 2021 18:20:03 +0300 Subject: [PATCH 05/11] enabling windows --- tests/main_test.py | 5 +++++ tests/test_arg_parser.py | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/tests/main_test.py b/tests/main_test.py index c33140a..2e5161d 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -281,11 +281,13 @@ def test_no_run_in_windows(self): main_entry_point(["run", "python", "-c", "pass"]) self.assertEqual(cm.exception.code, 2) + @unittest.skipUnless(is_posix, "not POSIX") def test_run_needs_venv(self): with self.assertRaises(VenvDoesNotExistExit) as cm: main_entry_point(windows_too(["run", "python", "-c", "pass"])) self.assertIsErrorExit(cm.exception) + @unittest.skipUnless(is_posix, "not POSIX") def test_run_exit_code_0(self): """Test that main_entry_point returns the same exit code, as the called command""" @@ -294,6 +296,7 @@ def test_run_exit_code_0(self): main_entry_point(windows_too(["run", "python3", "-c", "exit(0)"])) self.assertEqual(ce.exception.code, 0) + @unittest.skipUnless(is_posix, "not POSIX") def test_run_exit_code_1(self): """Test that main_entry_point returns the same exit code, as the called command""" @@ -302,6 +305,7 @@ def test_run_exit_code_1(self): main_entry_point(windows_too(["run", "python3", "-c", "exit(1)"])) self.assertEqual(ce.exception.code, 1) + @unittest.skipUnless(is_posix, "not POSIX") def test_run_exit_code_2(self): """Test that main_entry_point returns the same exit code, as the called command""" @@ -310,6 +314,7 @@ def test_run_exit_code_2(self): main_entry_point(windows_too(["run", "python3", "-c", "exit(2)"])) self.assertEqual(ce.exception.code, 2) + @unittest.skipUnless(is_posix, "not POSIX") def test_run_python_version(self): main_entry_point(["create"]) diff --git a/tests/test_arg_parser.py b/tests/test_arg_parser.py index cb01f5e..1f86166 100644 --- a/tests/test_arg_parser.py +++ b/tests/test_arg_parser.py @@ -8,12 +8,23 @@ from vien.main import get_project_dir from vien.arg_parser import Parsed, Commands + def windows_too(args: List[str]) -> List[str]: if is_windows: return [Parsed.PARAM_WINDOWS_ALL_ARGS] + args else: return args + +@unittest.skipUnless(is_windows, "Windows-only tests") +class TestWindowsAllArgs(unittest.TestCase): + def test_all_args_accepted_only_on_windows(self): + # is the PARAM_WINDOWS_ALL_ARGS is set, we must run windows. + # Otherwise, AssertionError is thrown + with self.assertRaises(AssertionError): + Parsed([Parsed.PARAM_WINDOWS_ALL_ARGS, 'shell']) + + class TestProjectDir(unittest.TestCase): def test_run_short_left(self): From 5cfcae2a474025cab759c075c1fc5ef74eb80923 Mon Sep 17 00:00:00 2001 From: rtmigo Date: Wed, 26 May 2021 18:26:12 +0300 Subject: [PATCH 06/11] enabling windows --- tests/main_test.py | 2 +- tests/test_arg_parser.py | 4 ++-- tests/test_get_project_dir.py | 3 ++- vien/arg_parser.py | 2 ++ 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/main_test.py b/tests/main_test.py index 2e5161d..39ba3d3 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -276,7 +276,7 @@ def test_recreate_resolves_python3(self): ############################################################################ @unittest.skipUnless(is_windows, "testing windows limitations") - def test_no_run_in_windows(self): + def test_no_run_command_in_windows(self): with self.assertRaises(SystemExit) as cm: main_entry_point(["run", "python", "-c", "pass"]) self.assertEqual(cm.exception.code, 2) diff --git a/tests/test_arg_parser.py b/tests/test_arg_parser.py index 1f86166..b0ea919 100644 --- a/tests/test_arg_parser.py +++ b/tests/test_arg_parser.py @@ -28,11 +28,11 @@ def test_all_args_accepted_only_on_windows(self): class TestProjectDir(unittest.TestCase): def test_run_short_left(self): - pd = Parsed('-p a/b/c run python3 myfile.py'.split()) + pd = Parsed(windows_too('-p a/b/c run python3 myfile.py'.split())) self.assertEqual(pd.project_dir_arg, 'a/b/c') def test_run_long_left(self): - pd = Parsed('--project-dir a/b/c run python3 myfile.py'.split()) + pd = Parsed(windows_too('--project-dir a/b/c run python3 myfile.py'.split())) self.assertEqual(pd.project_dir_arg, 'a/b/c') def test_call_short_right(self): diff --git a/tests/test_get_project_dir.py b/tests/test_get_project_dir.py index 074ae8a..2af2e32 100644 --- a/tests/test_get_project_dir.py +++ b/tests/test_get_project_dir.py @@ -3,6 +3,7 @@ from pathlib import Path from tests.common import is_posix +from tests.test_arg_parser import windows_too from vien.main import get_project_dir from vien.exceptions import PyFileArgNotFoundExit from vien.arg_parser import Parsed @@ -21,7 +22,7 @@ def setUp(self) -> None: def _gpd(self, cmd: str) -> Path: cmd = fix_paths(cmd) - result = get_project_dir(Parsed(cmd.split())) + result = get_project_dir(Parsed(windows_too(cmd.split()))) self.assertTrue(result.is_absolute()) return result diff --git a/vien/arg_parser.py b/vien/arg_parser.py index 6760116..03649e5 100644 --- a/vien/arg_parser.py +++ b/vien/arg_parser.py @@ -59,6 +59,8 @@ def __init__(self, args: Optional[List[str]]): if args is None: args = sys.argv[1:] + # secret parameter PARAM_WINDOWS_ALL_ARGS allows to run commands that + # are not yet fully supported on Windows. enable_windows_all_args = self.PARAM_WINDOWS_ALL_ARGS in args if enable_windows_all_args: # for more transparent testing, I don't want this param From 13240ae05ea517d1be7f9e312e8902f96e319e60 Mon Sep 17 00:00:00 2001 From: rtmigo Date: Wed, 26 May 2021 18:31:36 +0300 Subject: [PATCH 07/11] enabling windows --- tests/test_arg_parser.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/test_arg_parser.py b/tests/test_arg_parser.py index b0ea919..eed960a 100644 --- a/tests/test_arg_parser.py +++ b/tests/test_arg_parser.py @@ -10,15 +10,22 @@ def windows_too(args: List[str]) -> List[str]: + """For the Windows platform, appends a secret parameter to the arguments + so that Windows accepts even unsupported commands. + + This allows testing functionality that is only partially + implemented for Windows. + """ if is_windows: return [Parsed.PARAM_WINDOWS_ALL_ARGS] + args else: return args -@unittest.skipUnless(is_windows, "Windows-only tests") + class TestWindowsAllArgs(unittest.TestCase): - def test_all_args_accepted_only_on_windows(self): + @unittest.skipUnless(is_posix, "posix-only") + def test_windowsallargs_fails_on_posix(self): # is the PARAM_WINDOWS_ALL_ARGS is set, we must run windows. # Otherwise, AssertionError is thrown with self.assertRaises(AssertionError): From 9baf96dca9c32ee8aa1642a9892cdd606e691ad0 Mon Sep 17 00:00:00 2001 From: rtmigo Date: Wed, 26 May 2021 18:54:10 +0300 Subject: [PATCH 08/11] publish --- CHANGELOG.md | 4 ++++ README.md | 9 ++++++++- setup.py | 1 + tests/main_test.py | 11 +++++++---- tests/test_arg_parser.py | 4 ++-- vien/arg_parser.py | 2 +- vien/constants.py | 2 +- vien/main.py | 18 +----------------- 8 files changed, 25 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 639bd44..5164972 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 7.0 + +- The `create`, `call`, `delete`, `recreate` and `path` commands work on Windows. + # 6.0 - The `create` and `recreate` commands without arguments will create a virtual diff --git a/README.md b/README.md index 019931c..af9b8ee 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ [![PyPI version shields.io](https://img.shields.io/pypi/v/vien.svg)](https://pypi.python.org/pypi/vien/) -[![Generic badge](https://img.shields.io/badge/OS-Linux%20|%20macOS-blue.svg)](#) [![Generic badge](https://img.shields.io/badge/Python-3.7+-blue.svg)](#) +[![Generic badge](https://img.shields.io/badge/OS-Linux%20|%20macOS-blue.svg)](#) +[![Generic badge](https://img.shields.io/badge/OS-Windows-yellow.svg)](#) + # [vien](https://github.com/rtmigo/vien_py#readme) @@ -15,6 +17,11 @@ It provides one-line shortcuts for: ----- +`vien` supports Python 3.7+ on Linux and macOS. +In the case of Windows, all commands work, except for `run` and `shell`. + +----- + Switching between projects should be simple. Creating environments for the projects should be simple too. diff --git a/setup.py b/setup.py index 4f668bf..6801d63 100644 --- a/setup.py +++ b/setup.py @@ -56,6 +56,7 @@ def load_module_dict(filename: str) -> dict: "Typing :: Typed", "Topic :: Software Development :: Build Tools", "Operating System :: POSIX", + "Operating System :: Microsoft :: Windows" ], test_suite="test_unit.suite" diff --git a/tests/main_test.py b/tests/main_test.py index 39ba3d3..0619ea2 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -72,9 +72,6 @@ def test_path(self): main_entry_point(["path"]) - - - class TestsInsideTempProjectDir(unittest.TestCase): def setUp(self): @@ -276,7 +273,7 @@ def test_recreate_resolves_python3(self): ############################################################################ @unittest.skipUnless(is_windows, "testing windows limitations") - def test_no_run_command_in_windows(self): + def test_run_missing_in_windows(self): with self.assertRaises(SystemExit) as cm: main_entry_point(["run", "python", "-c", "pass"]) self.assertEqual(cm.exception.code, 2) @@ -518,6 +515,12 @@ def test_call_project_dir_relative_imports(self): ############################################################################ + @unittest.skipUnless(is_windows, "testing windows limitations") + def test_shell_missing_in_windows(self): + with self.assertRaises(SystemExit) as cm: + main_entry_point(["run", "shell"]) + self.assertEqual(cm.exception.code, 2) + @unittest.skipUnless(is_posix, "not POSIX") def test_shell_p(self): """Checking the -p changes both venv directory and the first item diff --git a/tests/test_arg_parser.py b/tests/test_arg_parser.py index eed960a..097c4e0 100644 --- a/tests/test_arg_parser.py +++ b/tests/test_arg_parser.py @@ -22,7 +22,6 @@ def windows_too(args: List[str]) -> List[str]: return args - class TestWindowsAllArgs(unittest.TestCase): @unittest.skipUnless(is_posix, "posix-only") def test_windowsallargs_fails_on_posix(self): @@ -39,7 +38,8 @@ def test_run_short_left(self): self.assertEqual(pd.project_dir_arg, 'a/b/c') def test_run_long_left(self): - pd = Parsed(windows_too('--project-dir a/b/c run python3 myfile.py'.split())) + pd = Parsed( + windows_too('--project-dir a/b/c run python3 myfile.py'.split())) self.assertEqual(pd.project_dir_arg, 'a/b/c') def test_call_short_right(self): diff --git a/vien/arg_parser.py b/vien/arg_parser.py index 03649e5..1f32d69 100644 --- a/vien/arg_parser.py +++ b/vien/arg_parser.py @@ -51,7 +51,7 @@ class Commands(Enum): class Parsed: - PARAM_WINDOWS_ALL_ARGS = "--windows-all-args" + PARAM_WINDOWS_ALL_ARGS = "--vien-secret-windows-all-args" def __init__(self, args: Optional[List[str]]): super().__init__() diff --git a/vien/constants.py b/vien/constants.py index c17f9b8..1cd7c93 100644 --- a/vien/constants.py +++ b/vien/constants.py @@ -1,3 +1,3 @@ -__version__ = "6.0.1" +__version__ = "7.0.0" __copyright__ = "(c) 2020-2021 Artëm IG " __license__ = "BSD-3-Clause" diff --git a/vien/main.py b/vien/main.py index 99478fd..2992c79 100755 --- a/vien/main.py +++ b/vien/main.py @@ -72,6 +72,7 @@ def run_bash_sequence(commands: List[str], env: Optional[Dict] = None) -> int: def run_cmdexe_sequence(commands: List[str], env: Optional[Dict] = None) -> int: # todo test independently + # This function does not work "officially" yet. need_windows() @@ -95,14 +96,6 @@ def run_cmdexe_sequence(commands: List[str], env: Optional[Dict] = None) -> int: env=env) -# def quote_shell_arg(arg: str) -> str: -# # todo two separate functions called on need -# if is_posix: -# return shlex.quote(arg) -# else: -# return cmd_escape_arg(arg) - - def venv_dir_to_python_exe(venv_dir: Path) -> Path: # this method is being tested indirectly each time the venv is created: # vien prints the path to executable after running this function @@ -292,8 +285,6 @@ def main_shell(dirs: Dirs, input: Optional[str], input_delay: Optional[float]): raise ChildExit(cp.returncode) -# def _run(dirs: Dirs, other_args: List[str]): - def bash_args_to_str(args: List[str]) -> str: return ' '.join(shlex.quote(arg) for arg in args) @@ -323,10 +314,6 @@ def main_run(dirs: Dirs, command: List[str]): if not activate_file.exists(): raise FileNotFoundError(activate_file) - # if prepend_py_path: - # commands.append(f'export PYTHONPATH="{prepend_py_path}:$PYTHONPATH"') - # sequence.append(" ".join(quote_shell_arg(a) for a in command)) - exit_code = run_func(sequence, env=child_env(dirs.project_dir)) raise ChildExit(exit_code) @@ -407,9 +394,6 @@ def get_project_dir(parsed: Parsed) -> Path: def main_entry_point(args: Optional[List[str]] = None): parsed = Parsed(args) - if is_windows: - print("WARNING: Windows is not yet fully supported.") - # todo replace private _ns attrs with public properties dirs = Dirs(project_dir=get_project_dir(parsed)) From 6b3125469f4b6ef659e1f9b18e5012a6036548ba Mon Sep 17 00:00:00 2001 From: rtmigo Date: Wed, 26 May 2021 18:56:22 +0300 Subject: [PATCH 09/11] publish --- README.md | 13 +++---------- setup.py | 2 +- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index af9b8ee..912883d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ -[![PyPI version shields.io](https://img.shields.io/pypi/v/vien.svg)](https://pypi.python.org/pypi/vien/) -[![Generic badge](https://img.shields.io/badge/Python-3.7+-blue.svg)](#) -[![Generic badge](https://img.shields.io/badge/OS-Linux%20|%20macOS-blue.svg)](#) -[![Generic badge](https://img.shields.io/badge/OS-Windows-yellow.svg)](#) - +`vien` supports Python 3.7+ on Linux and macOS. +In the case of Windows, all commands work, except for `run` and `shell`. +----- # [vien](https://github.com/rtmigo/vien_py#readme) @@ -17,11 +15,6 @@ It provides one-line shortcuts for: ----- -`vien` supports Python 3.7+ on Linux and macOS. -In the case of Windows, all commands work, except for `run` and `shell`. - ------ - Switching between projects should be simple. Creating environments for the projects should be simple too. diff --git a/setup.py b/setup.py index 6801d63..7575fe9 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ def load_module_dict(filename: str) -> dict: constants = load_module_dict(f'{name}/constants.py') readme = (Path(__file__).parent / 'README.md').read_text(encoding="utf-8") -readme = "# " + readme.partition("\n#")[-1] +#readme = "# " + readme.partition("\n#")[-1] setup( name=name, From 3d918b7e35ef14998bffe220fc0059c18662cc47 Mon Sep 17 00:00:00 2001 From: rtmigo Date: Wed, 26 May 2021 18:57:02 +0300 Subject: [PATCH 10/11] publish --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 912883d..2be1145 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ `vien` supports Python 3.7+ on Linux and macOS. In the case of Windows, all commands work, except for `run` and `shell`. + ----- # [vien](https://github.com/rtmigo/vien_py#readme) From 4ed2f740f343fb57b284cf47129bd3ac885ff725 Mon Sep 17 00:00:00 2001 From: rtmigo Date: Wed, 26 May 2021 18:57:52 +0300 Subject: [PATCH 11/11] publish --- vien/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/vien/constants.py b/vien/constants.py index 1cd7c93..97421c2 100644 --- a/vien/constants.py +++ b/vien/constants.py @@ -1,3 +1,4 @@ __version__ = "7.0.0" __copyright__ = "(c) 2020-2021 Artëm IG " __license__ = "BSD-3-Clause" +