Skip to content

Commit

Permalink
Merge d541d00 into master
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored May 25, 2021
2 parents f743eb8 + d541d00 commit 1e74bc7
Show file tree
Hide file tree
Showing 18 changed files with 175 additions and 45 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ jobs:

strategy:
matrix:
os: [ ubuntu-latest, macos-latest ]
# Windows is not really supported: many tests are skipped on Windows.
# But we still run the tests, gradually increasing the count
# of windows-compatible ones
os: [ ubuntu-latest, macos-latest, windows-latest ] #
python-version: [ 3.7, 3.9 ]

steps:
Expand All @@ -34,7 +37,8 @@ jobs:
python -m pip install --upgrade pip
python -m pip install flake8 mypy
pip install -e .
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
pip install -r requirements.txt
#if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ $ vien call -B -OO main.py arg1 arg2
```

The optional `-p` parameter can be specified before the `call` word. It allows
you to set the project directory **relative** to the parent directory of the **
file** being run.
you to set the project directory **relative** to the parent directory of the
**file** being run.

``` bash
$ cd any/where # working dir is irrelevant
Expand Down Expand Up @@ -284,7 +284,8 @@ If `--project-dir` is specified, it is the project directory.
If `--project-dir` is not specified, then all commands assume that the current
working directory is the project directory.

The next two calls use the same project directory and the same virtual environment. However, the working directory is different.
The next two calls use the same project directory and the same virtual
environment. However, the working directory is different.

``` bash
cd /abc/myProject
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def load_module_dict(filename: str) -> dict:
name = "vien"
constants = load_module_dict(f'{name}/constants.py')

readme = (Path(__file__).parent / 'README.md').read_text()
readme = (Path(__file__).parent / 'README.md').read_text(encoding="utf-8")
readme = "# " + readme.partition("\n#")[-1]

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

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


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

# python3 -m unittest svet.bash_runner_test
Expand Down
1 change: 1 addition & 0 deletions tests/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from vien import is_posix
13 changes: 8 additions & 5 deletions tests/main_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
from tempfile import TemporaryDirectory
from timeit import default_timer as timer

from tests.common import is_posix
from vien import main_entry_point
from vien.exceptions import ChildExit, VenvExistsExit, VenvDoesNotExistExit, \
PyFileNotFoundExit
from tests.time_limited import TimeLimited


class CapturedOutput:
"""Captures output of python functions (not a child process output,
but own output)."""
# ? maybe replace with https://pypi.org/project/stream-redirect/
def __init__(self):
self._new_out = StringIO()
Expand All @@ -43,7 +46,7 @@ def std(self) -> str:
def err(self) -> str:
return self._new_err.getvalue()


#@unittest.skipUnless(is_posix(), "not POSIX")
class Test(unittest.TestCase):
def test_no_args(self):
with self.assertRaises(SystemExit) as cp:
Expand All @@ -66,6 +69,7 @@ def test_path(self):
# return len(inner_str) > len(outer_str) and inner_str.startswith(outer_str)


@unittest.skipUnless(is_posix(), "not POSIX")
class TestsInsideTempProjectDir(unittest.TestCase):

def setUp(self):
Expand Down Expand Up @@ -214,8 +218,9 @@ def test_shell_fails_if_not_exist(self):
self.assertIsErrorExit(cm.exception)

def test_run_needs_venv(self):
with self.assertRaises(VenvDoesNotExistExit):
main_entry_point(["run", "python", "--version"])
with self.assertRaises(VenvDoesNotExistExit) as cm:
main_entry_point(["run", "python", "-c", "pass"])
self.assertIsErrorExit(cm.exception)

def test_run_p(self):
"""Checking the -p changes both venv directory and the first item
Expand Down Expand Up @@ -502,8 +507,6 @@ def test_shell_exit_code_zero(self):
self.assertFalse(ce.exception.code, 0)

def test_shell_but_no_venv(self):
# python3 -m unittest svet.main_test.TestsInsideTempProjectDir.test_shell

with TimeLimited(10): # safety net
with self.assertRaises(VenvDoesNotExistExit) as cm:
main_entry_point(["shell"])
Expand Down
17 changes: 17 additions & 0 deletions tests/systems_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import unittest
from pathlib import PosixPath, WindowsPath, Path

from vien._common import need_posix, NotPosixError

is_cwd_posix = isinstance(Path().cwd(), PosixPath)
is_cwd_windows = isinstance(Path().cwd(), WindowsPath)


class Test(unittest.TestCase):

def test_need_posix(self):
if is_cwd_posix:
need_posix() # no exception
else:
with self.assertRaises(NotPosixError):
need_posix()
30 changes: 28 additions & 2 deletions tests/test_arg_parser.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import unittest
from pathlib import Path

from tests.common import is_posix
from vien.main import get_project_dir
from vien.arg_parser import Parsed
from vien.arg_parser import Parsed, Commands


class TestProjectDir(unittest.TestCase):
Expand Down Expand Up @@ -36,7 +37,7 @@ def test_call_short_both(self):
self.assertEqual(pd.project_dir_arg, 'd/e/f')


class TestCallOtherArgs(unittest.TestCase):
class TestParseCall(unittest.TestCase):

def test_outdated_p(self):
pd = Parsed('-p a/b/c call -p d/e/f myfile.py arg1 arg2'.split())
Expand All @@ -53,7 +54,32 @@ def test_unrecoginzed(self):
self.assertEqual(ce.exception.code, 2)


class TestParseShell(unittest.TestCase):
def test_no_args(self):
pd = Parsed('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'])
self.assertEqual(pd.shell_input, 'cd / && ls')

def test_delay(self):
pd = Parsed('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())
self.assertEqual(ce.exception.code, 2)


class TestParseRun(unittest.TestCase):
def test(self):
pd = Parsed(['run', 'python3', '-OO', 'file.py'])
self.assertEqual(pd.command, Commands.run)
self.assertEqual(pd.run_args, ['python3', '-OO', 'file.py'])


if __name__ == "__main__":
Expand Down
1 change: 1 addition & 0 deletions tests/test_call_parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import unittest

from tests.common import is_posix
from vien.call_parser import items_after, call_pyfile


Expand Down
30 changes: 19 additions & 11 deletions tests/test_get_project_dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,25 @@
import unittest
from pathlib import Path

from tests.common import is_posix
from vien.main import get_project_dir
from vien.exceptions import PyFileArgNotFoundExit
from vien.arg_parser import Parsed


def fix_paths(s: str):
if is_posix():
s = s.replace('W:/', '/')
return s


class TestGetProjectDir(unittest.TestCase):

def setUp(self) -> None:
os.chdir(os.path.dirname(__file__))

def _gpd(self, cmd: str) -> Path:
cmd = fix_paths(cmd)
result = get_project_dir(Parsed(cmd.split()))
self.assertTrue(result.is_absolute())
return result
Expand All @@ -30,28 +38,28 @@ def test_run_relative(self):
self.assertEqual(pd, Path.cwd() / "abc" / "def")

def test_run_absolute(self):
pd = self._gpd('-p /abc/def run python3 myfile.py')
self.assertEqual(pd, Path('/abc/def'))
pd = self._gpd('-p W:/abc/def run python3 myfile.py')
self.assertEqual(pd, Path(fix_paths('W:/abc/def')))

def test_call_no_file(self):
with self.assertRaises(PyFileArgNotFoundExit):
self._gpd('-p /aa/bb/proj call python3 --version')
self._gpd('-p W:/aa/bb/proj call python3 --version')

def test_call_file_abs_proj_abs(self):
pd = self._gpd('-p /aa/bb/proj call python3 /xx/yy/file.py')
self.assertEqual(pd, Path('/aa/bb/proj'))
pd = self._gpd('-p W:/aa/bb/proj call python3 W:/xx/yy/file.py')
self.assertEqual(pd, Path(fix_paths('W:/aa/bb/proj')))

def test_call_file_abs_proj_rel(self):
pd = self._gpd('-p aa/bb/proj call python3 /xx/yy/file.py')
self.assertEqual(pd, Path('/xx/yy/aa/bb/proj'))
pd = self._gpd('-p aa/bb/proj call python3 W:/xx/yy/file.py')
self.assertEqual(pd, Path(fix_paths('W:/xx/yy/aa/bb/proj')))

def test_call_file_abs_proj_rel_dots(self):
pd = self._gpd('-p .. call python3 /abc/project/pkg/file.py')
self.assertEqual(pd, Path('/abc/project'))
pd = self._gpd('-p .. call python3 W:/abc/project/pkg/file.py')
self.assertEqual(pd, Path(fix_paths('W:/abc/project')))

def test_call_file_rel_proj_abs(self):
pd = self._gpd('-p /aa/bb/proj call python3 xx/yy/file.py')
self.assertEqual(pd, Path('/aa/bb/proj'))
pd = self._gpd('-p W:/aa/bb/proj call python3 xx/yy/file.py')
self.assertEqual(pd, Path(fix_paths('W:/aa/bb/proj')))

def test_call_file_rel_proj_rel(self):
pd = self._gpd('-p aa/bb/proj call python3 xx/yy/file.py')
Expand Down
3 changes: 2 additions & 1 deletion tests/vien_dir_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
import unittest
from pathlib import Path

from tests.common import is_posix
from vien.main import get_vien_dir


@unittest.skipUnless(is_posix(), "not POSIX")
class TestVenvsDir(unittest.TestCase):

def test_if_set_plain(self):
Expand Down
2 changes: 2 additions & 0 deletions vien/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
from .constants import __version__, __license__, __copyright__
from ._common import is_posix
from .main import main_entry_point

26 changes: 26 additions & 0 deletions vien/_common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from typing import Optional

__is_posix_value: Optional[bool] = None


class NotPosixError(Exception):
pass


def is_posix() -> bool:
global __is_posix_value
if __is_posix_value is None:
try:
import posix
__is_posix_value = True
except ImportError:
__is_posix_value = False

assert __is_posix_value is not None

return __is_posix_value


def need_posix():
if not is_posix():
raise NotPosixError
20 changes: 20 additions & 0 deletions vien/arg_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,23 @@ def python_executable(self) -> str:
raise RuntimeError
assert self._ns.python is not None
return self._ns.python

@property
def shell_input(self) -> Optional[str]:
if self.command != Commands.shell:
raise RuntimeError
return self._ns.input

@property
def shell_delay(self) -> Optional[float]:
if self.command != Commands.shell:
raise RuntimeError
return self._ns.delay

@property
def run_args(self) -> List[str]:
if self.command != Commands.run:
raise RuntimeError
return self._ns.otherargs


8 changes: 5 additions & 3 deletions vien/bash_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@
import time
from subprocess import Popen, TimeoutExpired, CalledProcessError, \
CompletedProcess, PIPE
from typing import Optional, Dict

from vien._common import need_posix


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

need_posix()

# 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',
Expand Down
2 changes: 1 addition & 1 deletion vien/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__version__ = "5.0.2"
__version__ = "5.0.3"
__copyright__ = "(c) 2020-2021 Artëm IG <github.com/rtmigo>"
__license__ = "BSD-3-Clause"
7 changes: 5 additions & 2 deletions vien/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from pathlib import Path

#from vien.main import exe_name

# from vien.main import exe_name




class VienExit(SystemExit):
Expand Down Expand Up @@ -52,4 +55,4 @@ def __init__(self, path: Path):

class CannotFindExecutableExit(VienExit):
def __init__(self, version: str):
super().__init__(f"Cannot resolve '{version}' to an executable file.")
super().__init__(f"Cannot resolve '{version}' to an executable file.")
Loading

0 comments on commit 1e74bc7

Please sign in to comment.