Skip to content

Commit 65553fe

Browse files
Merge 835d520 into master
2 parents 0c706ac + 835d520 commit 65553fe

File tree

4 files changed

+156
-126
lines changed

4 files changed

+156
-126
lines changed

vien/_constants.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
__version__ = "8.0.4"
1+
__version__ = "8.0.5"
22
__copyright__ = "(c) 2020-2021 Artëm IG <github.com/rtmigo>"
33
__license__ = "BSD-3-Clause"
4-

vien/_exceptions.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ def __init__(self, exit_code: int):
2020
super().__init__(exit_code)
2121

2222

23-
class VenvExistsExit(VienExit): # todo does it return error code?
24-
pass
23+
class VenvExistsExit(VienExit):
24+
def __init__(self, path: Path):
25+
super().__init__(f'Virtual environment "{path}" already exists.')
2526

2627

2728
class VenvDoesNotExistExit(VienExit):
2829
def __init__(self, path: Path):
2930
super().__init__(f'Virtual environment "{path}" does not exist.\n'
30-
f'You can create it with "create" command.')
31+
f'You can create it with "vien create".')
3132

3233

3334
class PyFileNotFoundExit(VienExit):
@@ -43,12 +44,12 @@ def __init__(self):
4344

4445
class FailedToCreateVenvExit(VienExit):
4546
def __init__(self, path: Path):
46-
super().__init__(f"Failed to create virtualenv {path}.")
47+
super().__init__(f"Failed to create virtual environment {path}.")
4748

4849

4950
class FailedToClearVenvExit(VienExit):
5051
def __init__(self, path: Path):
51-
super().__init__(f"Failed to clear virtualenv {path}.")
52+
super().__init__(f"Failed to clear virtual environment {path}.")
5253

5354

5455
class CannotFindExecutableExit(VienExit):

vien/_main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def arg_to_python_interpreter(argument: Optional[str]) -> str:
139139

140140
def main_create(venv_dir: Path, interpreter: Optional[str]):
141141
if venv_dir.exists():
142-
raise VenvExistsExit("Virtualenv already exists.")
142+
raise VenvExistsExit(venv_dir)
143143

144144
exe = arg_to_python_interpreter(interpreter)
145145

vien/_parsed_args.py

Lines changed: 148 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# SPDX-License-Identifier: BSD-3-Clause
33

44
import argparse
5+
import os
56
import sys
67
from enum import Enum
78
from typing import List, Optional, Iterable
@@ -65,128 +66,157 @@ class Commands(Enum):
6566
path = "path"
6667

6768

69+
class TempColumns:
70+
def __init__(self, width: int):
71+
self.width = width
72+
self._old_value: Optional[str] = None
73+
74+
def __enter__(self):
75+
self._old_value = os.environ.get('COLUMNS')
76+
77+
new_value_int = self.width
78+
79+
try:
80+
old_value_int = int(self._old_value)
81+
new_value_int = min(old_value_int, new_value_int)
82+
except (ValueError, TypeError):
83+
pass
84+
85+
os.environ['COLUMNS'] = str(new_value_int)
86+
87+
def __exit__(self, exc_type, exc_val, exc_tb):
88+
if self._old_value is not None:
89+
os.environ['COLUMNS'] = self._old_value
90+
91+
6892
class ParsedArgs:
6993
PARAM_WINDOWS_ALL_ARGS = "--vien-secret-windows-all-args"
7094

7195
def __init__(self, args: Optional[List[str]]):
72-
super().__init__()
73-
74-
self._call: Optional[ParsedCall] = None
75-
76-
if args is None:
77-
args = sys.argv[1:]
78-
79-
# secret parameter PARAM_WINDOWS_ALL_ARGS allows to run commands that
80-
# are not yet fully supported on Windows.
81-
enable_windows_all_args = self.PARAM_WINDOWS_ALL_ARGS in args
82-
if enable_windows_all_args:
83-
# for more transparent testing, I don't want this param
84-
# to ever affect posix behavior
85-
assert is_windows
86-
87-
parser = argparse.ArgumentParser()
88-
89-
parser.add_argument("--project-dir", "-p", default=None, type=str,
90-
help="the Python project directory "
91-
"(default: current working directory). "
92-
"Implicitly determines which virtual "
93-
"environment should be used for the command")
94-
95-
# the following parameter is added only to avoid parsing errors.
96-
# Actually we use its value from `args` before running ArgumentParser
97-
parser.add_argument(self.PARAM_WINDOWS_ALL_ARGS, action='store_true',
98-
help=argparse.SUPPRESS)
99-
100-
subparsers = parser.add_subparsers(dest='command', required=True)
101-
102-
parser_init = subparsers.add_parser(Commands.create.name,
103-
help="create new virtual environment")
104-
parser_init.add_argument('python', type=str, default=None,
105-
nargs='?')
106-
107-
subparsers.add_parser(Commands.delete.name,
108-
help="delete existing environment")
109-
110-
parser_reinit = subparsers.add_parser(
111-
Commands.recreate.name,
112-
help="delete existing environment and create new")
113-
parser_reinit.add_argument('python', type=str, default=None,
114-
nargs='?')
115-
116-
if is_posix or enable_windows_all_args:
117-
shell_parser = subparsers.add_parser(
118-
Commands.shell.name,
119-
help="dive into Bash sub-shell with the environment")
120-
shell_parser.add_argument("--input", type=str, default=None)
121-
shell_parser.add_argument("--delay", type=float, default=None,
122-
help=argparse.SUPPRESS)
123-
124-
if is_posix or enable_windows_all_args:
125-
parser_run = subparsers.add_parser(
126-
Commands.run.name,
127-
help="run a shell command in the environment")
128-
parser_run.add_argument('otherargs', nargs=argparse.REMAINDER)
129-
130-
parser_call = subparsers.add_parser(
131-
Commands.call.name,
132-
help="run a .py file in the environment")
133-
# todo Remove it later. [call -p] is outdated since 2021-05
134-
parser_call.add_argument("--project-dir", "-p", default=None, type=str,
135-
dest="outdated_call_project_dir",
136-
help=argparse.SUPPRESS)
137-
# this arg is for help only. Actually it's buggy (at least in 3.7),
138-
# so we will never use its result, and get those args other way
139-
parser_call.add_argument('args_to_python', nargs=argparse.REMAINDER)
140-
141-
subparsers.add_parser(
142-
Commands.path.name,
143-
help="show the path of the environment "
144-
"for the project")
145-
146-
if not args:
147-
print(usage_doc())
148-
parser.print_help()
149-
exit(2)
150-
151-
self.args = args
152-
153-
# it seems, nargs.REMAINDER is buggy in 2021:
154-
# https://bugs.python.org/issue17050
155-
#
156-
# For example, when the first REMAINDER argument is an option
157-
# such as "-d", argparse shows error instead of just remembering "-d"
158-
#
159-
# But "-d" actually can be the first REMAINDER arg after the CALL
160-
# command.
161-
#
162-
# That's why we parse args twice. First time with `parse_known_args` -
163-
# to get the command name. And then, if it's not CALL - we parse
164-
# again with a stricter parse_args.
165-
166-
self._ns: argparse.Namespace
167-
unknown: List[str]
168-
169-
self._ns, unknown = parser.parse_known_args(self.args)
170-
if self._ns.command == 'call':
171-
self.args_to_python = list(_iter_after(args, 'call'))
172-
173-
# if some of the unknown args are NOT after the 'call',
174-
# then we're failed to interpret the command
175-
bad_unrecognized = [unk for unk in unknown if
176-
unk not in self.args_to_python]
177-
if bad_unrecognized:
178-
parser.error(f"unrecognized arguments: {bad_unrecognized}")
179-
raise AssertionError("Not expected to run this line")
180-
181-
# todo Remove later. [call -p] is outdated since 2021-05
182-
self.args_to_python = _remove_leading_p(self.args_to_python)
183-
self._call = ParsedCall(args)
184-
else:
185-
# if some args were not recognized, parsing everything stricter
186-
if unknown:
187-
self._ns = parser.parse_args(self.args)
188-
189-
self.command = Commands(self._ns.command)
96+
with TempColumns(80):
97+
98+
self._call: Optional[ParsedCall] = None
99+
100+
if args is None:
101+
args = sys.argv[1:]
102+
103+
# secret parameter PARAM_WINDOWS_ALL_ARGS allows to run commands that
104+
# are not yet fully supported on Windows.
105+
enable_windows_all_args = self.PARAM_WINDOWS_ALL_ARGS in args
106+
if enable_windows_all_args:
107+
# for more transparent testing, I don't want this param
108+
# to ever affect posix behavior
109+
assert is_windows
110+
111+
parser = argparse.ArgumentParser()
112+
113+
parser.add_argument("--project-dir", "-p", default=None, type=str,
114+
help="the Python project directory "
115+
"(default: current working directory). "
116+
"Implicitly determines which virtual "
117+
"environment should be used for the "
118+
"command")
119+
120+
# the following parameter is added only to avoid parsing errors.
121+
# Actually we use its value from `args` before running
122+
# ArgumentParser
123+
parser.add_argument(self.PARAM_WINDOWS_ALL_ARGS,
124+
action='store_true',
125+
help=argparse.SUPPRESS)
126+
127+
subparsers = parser.add_subparsers(dest='command', required=True)
128+
129+
parser_init = subparsers.add_parser(
130+
Commands.create.name,
131+
help="create new virtual environment")
132+
parser_init.add_argument('python', type=str, default=None,
133+
nargs='?')
134+
135+
subparsers.add_parser(Commands.delete.name,
136+
help="delete existing environment")
137+
138+
parser_reinit = subparsers.add_parser(
139+
Commands.recreate.name,
140+
help="delete existing environment and create new")
141+
parser_reinit.add_argument('python', type=str, default=None,
142+
nargs='?')
143+
144+
if is_posix or enable_windows_all_args:
145+
shell_parser = subparsers.add_parser(
146+
Commands.shell.name,
147+
help="dive into Bash sub-shell with the environment")
148+
shell_parser.add_argument("--input", type=str, default=None)
149+
shell_parser.add_argument("--delay", type=float, default=None,
150+
help=argparse.SUPPRESS)
151+
152+
if is_posix or enable_windows_all_args:
153+
parser_run = subparsers.add_parser(
154+
Commands.run.name,
155+
help="run a shell command in the environment")
156+
parser_run.add_argument('otherargs', nargs=argparse.REMAINDER)
157+
158+
parser_call = subparsers.add_parser(
159+
Commands.call.name,
160+
help="run a .py file in the environment")
161+
# todo Remove it later. [call -p] is outdated since 2021-05
162+
parser_call.add_argument("--project-dir", "-p", default=None,
163+
type=str,
164+
dest="outdated_call_project_dir",
165+
help=argparse.SUPPRESS)
166+
# this arg is for help only. Actually it's buggy (at least in 3.7),
167+
# so we will never use its result, and get those args other way
168+
parser_call.add_argument('args_to_python', nargs=argparse.REMAINDER)
169+
170+
subparsers.add_parser(
171+
Commands.path.name,
172+
help="show the path of the environment "
173+
"for the project")
174+
175+
if not args:
176+
print(usage_doc())
177+
parser.print_help()
178+
exit(2)
179+
180+
self.args = args
181+
182+
# it seems, nargs.REMAINDER is buggy in 2021:
183+
# https://bugs.python.org/issue17050
184+
#
185+
# For example, when the first REMAINDER argument is an option
186+
# such as "-d", argparse shows error instead of just
187+
# remembering "-d"
188+
#
189+
# But "-d" actually can be the first REMAINDER arg after the CALL
190+
# command.
191+
#
192+
# That's why we parse args twice. First time with
193+
# `parse_known_args` - to get the command name. And then, if it's
194+
# not CALL - we parse again with a stricter parse_args.
195+
196+
self._ns: argparse.Namespace
197+
unknown: List[str]
198+
199+
self._ns, unknown = parser.parse_known_args(self.args)
200+
if self._ns.command == 'call':
201+
self.args_to_python = list(_iter_after(args, 'call'))
202+
203+
# if some of the unknown args are NOT after the 'call',
204+
# then we're failed to interpret the command
205+
bad_unrecognized = [unk for unk in unknown if
206+
unk not in self.args_to_python]
207+
if bad_unrecognized:
208+
parser.error(f"unrecognized arguments: {bad_unrecognized}")
209+
raise AssertionError("Not expected to run this line")
210+
211+
# todo Remove later. [call -p] is outdated since 2021-05
212+
self.args_to_python = _remove_leading_p(self.args_to_python)
213+
self._call = ParsedCall(args)
214+
else:
215+
# if some args were not recognized, parsing everything stricter
216+
if unknown:
217+
self._ns = parser.parse_args(self.args)
218+
219+
self.command = Commands(self._ns.command)
190220

191221
# @property
192222
# def command(self) -> Commands:

0 commit comments

Comments
 (0)