Skip to content

Commit

Permalink
Merge 4ed2f74 into master
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored May 26, 2021
2 parents 81f5cad + 4ed2f74 commit 11eb5f6
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 57 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[![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)](#)
`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)

Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"
Expand Down
33 changes: 28 additions & 5 deletions tests/main_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
from pathlib import Path
from tempfile import TemporaryDirectory
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

from tests.common import is_posix
from tests.time_limited import TimeLimited
Expand Down Expand Up @@ -266,42 +272,53 @@ def test_recreate_resolves_python3(self):

############################################################################

@unittest.skipUnless(is_windows, "testing windows limitations")
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)

@unittest.skipUnless(is_posix, "not POSIX")
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)

@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"""
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)

@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"""
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)

@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"""
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)

@unittest.skipUnless(is_posix, "not POSIX")
def test_run_python_version(self):
main_entry_point(["create"])

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):
Expand Down Expand Up @@ -498,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
Expand Down
40 changes: 33 additions & 7 deletions tests/test_arg_parser.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,45 @@
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]:
"""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


class TestWindowsAllArgs(unittest.TestCase):
@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):
Parsed([Parsed.PARAM_WINDOWS_ALL_ARGS, 'shell'])


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):
Expand Down Expand Up @@ -56,28 +82,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'])

Expand Down
3 changes: 2 additions & 1 deletion tests/test_get_project_dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
51 changes: 35 additions & 16 deletions vien/arg_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
from enum import Enum
from typing import List, Optional

from vien._common import is_windows

from vien import is_posix

import vien
from vien.call_parser import items_after

Expand Down Expand Up @@ -47,13 +51,31 @@ class Commands(Enum):


class Parsed:
PARAM_WINDOWS_ALL_ARGS = "--vien-secret-windows-all-args"

def __init__(self, args: Optional[List[str]]):
super().__init__()

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
# to ever affect posix behavior
assert is_windows

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,
Expand All @@ -70,17 +92,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,
Expand All @@ -98,9 +122,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()
Expand Down Expand Up @@ -164,7 +185,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
Expand All @@ -184,5 +205,3 @@ def run_args(self) -> List[str]:
if self.command != Commands.run:
raise RuntimeError
return self._ns.otherargs


3 changes: 2 additions & 1 deletion vien/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
__version__ = "6.0.1"
__version__ = "7.0.0"
__copyright__ = "(c) 2020-2021 Artëm IG <github.com/rtmigo>"
__license__ = "BSD-3-Clause"

Loading

0 comments on commit 11eb5f6

Please sign in to comment.