1
0
Fork 0
mirror of https://github.com/python-poetry/install.python-poetry.org.git synced 2024-11-25 15:30:56 -05:00

feat: log stderr

This commit is contained in:
Brian Lee 2022-11-04 17:57:21 -07:00
parent 89f4049e20
commit 8db565cd0f
No known key found for this signature in database
GPG key ID: F99A54747DCA2D86
4 changed files with 212 additions and 8 deletions

View file

@ -19,6 +19,27 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
unit-test:
name: ${{ matrix.python-version }}
runs-on: Ubuntu
strategy:
matrix:
python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ]
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install pytest
run: python -m pip install pytest
- name: Run PyTest
run: python -m pytest ./tests
default:
name: ${{ matrix.os }} / ${{ matrix.python-version }} / install-poetry.py ${{ matrix.args }}
runs-on: ${{ matrix.image }}

View file

@ -83,6 +83,16 @@ You can also install Poetry for a `git` repository by using the `--git` option:
curl -sSL https://install.python-poetry.org | python3 - --git https://github.com/python-poetry/poetry.git@master
````
If you need Poetry to write errors to stderr, you can use `--stderr` option or the `$POETRY_LOG_STDERR`
environment variable:
> _Note: In CI environments, this will be enabled automatically._
```bash
curl -sSL https://install.python-poetry.org | python3 - --stderr
curl -sSL https://install.python-poetry.org | POETRY_LOG_STDERR=1 python3 -
````
> **Note**: The installer does not support Python < 3.6.
## Known Issues

View file

@ -898,6 +898,15 @@ def main():
"of Poetry available online."
),
)
parser.add_argument(
"--stderr",
dest="stderr",
action="store_true",
help=(
"Log installation errors to stderr instead of a log file."
),
default=False
)
args = parser.parse_args()
@ -912,6 +921,15 @@ def main():
git=args.git,
)
disable_log_file = args.stderr or string_to_bool(os.getenv("POETRY_LOG_STDERR", "0"))
if not disable_log_file and string_to_bool(os.getenv("CI", "0")):
installer._write(
colorize("info", "CI environment detected. Writing logs to stderr.")
)
disable_log_file = True
if args.uninstall or string_to_bool(os.getenv("POETRY_UNINSTALL", "0")):
return installer.uninstall()
@ -923,6 +941,14 @@ def main():
if e.log is not None:
import traceback
error = (
f"{e.log}\n"
f"Traceback:\n\n{''.join(traceback.format_tb(e.__traceback__))}"
)
if disable_log_file:
installer._write(colorize("error", error))
else:
_, path = tempfile.mkstemp(
suffix=".log",
prefix="poetry-installer-error-",
@ -930,8 +956,7 @@ def main():
text=True,
)
installer._write(colorize("error", f"See {path} for error logs."))
text = f"{e.log}\nTraceback:\n\n{''.join(traceback.format_tb(e.__traceback__))}"
Path(path).write_text(text)
Path(path).write_text(error)
return e.return_code

View file

@ -0,0 +1,148 @@
from unittest.mock import MagicMock, patch
import re
import typing
import importlib
import unittest
module = importlib.import_module("install-poetry")
class InstallPoetryTestCase(unittest.TestCase):
def setUp(self):
self.__patchers = []
self.__patchers.append(patch("install-poetry.Installer"))
self.__mock_installer_cls = self.__patchers[-1].start()
self.__installer = MagicMock()
self.__mock_installer_cls.return_value = self.__installer
self.__patchers.append(patch("install-poetry.argparse.ArgumentParser"))
self.__mock_argument_parser_cls = self.__patchers[-1].start()
self.__parser = MagicMock()
self.__args = MagicMock()
self.__args.version = None
self.__args.preview = False
self.__args.force = False
self.__args.accept_all = False
self.__args.path = None
self.__args.git = None
self.__args.stderr = False
self.__args.uninstall = False
self.__mock_argument_parser_cls.return_value = self.__parser
self.__parser.parse_args.return_value = self.__args
self.__patchers.append(patch("install-poetry.os"))
self.__mock_os = self.__patchers[-1].start()
self.__mock_os.getenv.side_effect = self.__getenv
self.__env = {}
self.__patchers.append(patch("install-poetry.colorize"))
self.__mock_colorize = self.__patchers[-1].start()
self.__mock_colorize.side_effect = self.__colorize
self.__messages = []
self.__patchers.append(patch("install-poetry.tempfile"))
self.__mock_tempfile = self.__patchers[-1].start()
self.__mock_tempfile.mkstemp.side_effect = self.__mkstemp
self.__tmp_file = None
self.__patchers.append(patch("install-poetry.Path"))
self.__mock_path_cls = self.__patchers[-1].start()
def tearDown(self):
for patcher in self.__patchers:
patcher.stop()
def test_install_poetry_main__happy(self):
self.__installer.run.return_value = 0
return_code = module.main()
self.__mock_installer_cls.assert_called_with(
version=None,
preview=False,
force=False,
accept_all=True,
path=None,
git=None
)
self.__installer.uninstall.assert_not_called()
self.assertEqual(return_code, 0)
def test_install_poetry_main__default_install_error(self):
self.__installer.run.side_effect = [
module.PoetryInstallationError(1, "a fake poetry installation error")
]
return_code = module.main()
self.__assert_no_matching_message("error", re.compile("a fake poetry installation error"))
self.__assert_any_matching_message("error", re.compile(f"See {self.__tmp_file} for error logs"))
self.assertEqual(return_code, 1)
self.__mock_path_cls(self.__tmp_file).write_text.assert_called_once()
def test_install_poetry_main__stderr_arg(self):
self.__args.stderr = True
self.__installer.run.side_effect = [
module.PoetryInstallationError(1, "a fake poetry installation error")
]
return_code = module.main()
self.__assert_no_matching_message("info", re.compile("CI environment detected"))
self.__assert_any_matching_message("error", re.compile("a fake poetry installation error"))
self.assertEqual(return_code, 1)
self.__mock_path_cls.assert_not_called()
def test_install_poetry_main__log_stderr_var(self):
self.__env["POETRY_LOG_STDERR"] = "1"
self.__installer.run.side_effect = [
module.PoetryInstallationError(1, "a fake poetry installation error")
]
return_code = module.main()
self.__assert_no_matching_message("info", re.compile("CI environment detected"))
self.__assert_any_matching_message("error", re.compile("a fake poetry installation error"))
self.assertEqual(return_code, 1)
self.__mock_path_cls.assert_not_called()
def test_install_poetry_main__ci(self):
self.__env["CI"] = "1"
self.__installer.run.side_effect = [
module.PoetryInstallationError(1, "a fake poetry installation error")
]
return_code = module.main()
self.__assert_any_matching_message("info", re.compile("CI environment detected"))
self.__assert_any_matching_message("error", re.compile("a fake poetry installation error"))
self.assertEqual(return_code, 1)
self.__mock_path_cls.assert_not_called()
def __colorize(self, severity: str, message: str) -> str:
self.__messages.append((severity, message))
return f"{severity}:{message}"
def __getenv(self, key: str, default: typing.Optional[str] = None) -> typing.Optional[str]:
return self.__env.get(key, default)
def __mkstemp(self, suffix="suffix", prefix="prefix", dir=None, text=None):
self.__tmp_file = f"{prefix}unittest{suffix}"
return None, self.__tmp_file
def __assert_any_matching_message(self, severity: str, pattern: re.Pattern):
self.assertGreater(self.__count_matching_message(severity, pattern), 0)
def __assert_no_matching_message(self, severity: str, pattern: re.Pattern):
self.assertEqual(self.__count_matching_message(severity, pattern), 0)
def __count_matching_message(self, severity: str, pattern: re.Pattern):
return len([message for message in self.__messages if severity == message[0] and pattern.search(message[1])])