Commit 063d19aa by Sébastien Eustace Committed by GitHub

Improve UI (#2230)

* Upgrade cleo and clikit to the latest version

* Tweak colors

* Format logs from poetry-core
parent 424fdbf7
......@@ -138,10 +138,10 @@ description = "Cleo allows you to create beautiful and testable command-line int
name = "cleo"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.7.6"
version = "0.8.0"
[package.dependencies]
clikit = ">=0.4.0,<0.5.0"
clikit = ">=0.5.0,<0.6.0"
[[package]]
category = "dev"
......@@ -158,7 +158,7 @@ description = "CliKit is a group of utilities to build beautiful and testable co
name = "clikit"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.4.3"
version = "0.5.1"
[package.dependencies]
pastel = ">=0.2.0,<0.3.0"
......@@ -176,6 +176,10 @@ version = ">=3.6,<4.0"
python = ">=3.5.0,<3.5.4"
version = ">=3.6,<4.0"
[package.dependencies.woops]
python = ">=3.6,<4.0"
version = ">=0.2.1,<0.3.0"
[[package]]
category = "dev"
description = "Cross-platform colored terminal text."
......@@ -1305,6 +1309,15 @@ version = "0.5.1"
[[package]]
category = "main"
description = "Handle and manage Python errors with ease"
marker = "python_version >= \"3.6\" and python_version < \"4.0\""
name = "woops"
optional = false
python-versions = ">=3.6,<4.0"
version = "0.2.1"
[[package]]
category = "main"
description = "Backport of pathlib-compatible object wrapper for zip files"
marker = "python_version >= \"3.5\" and python_version < \"3.8\" or python_version < \"3.8\""
name = "zipp"
......@@ -1322,7 +1335,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
testing = ["pathlib2", "unittest2", "jaraco.itertools", "func-timeout"]
[metadata]
content-hash = "35617c80a428d8f081214fcbe428540df288e67e5596531cba40415f19438d57"
content-hash = "7ae766406ba58bc44d4b0d5876927ab0db9005d6a80efab83ddd6a2c50c2f8c8"
python-versions = "~2.7 || ^3.5"
[metadata.files]
......@@ -1397,16 +1410,16 @@ chardet = [
{file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"},
]
cleo = [
{file = "cleo-0.7.6-py2.py3-none-any.whl", hash = "sha256:9443d67e5b2da79b32d820ae41758dd6a25618345cb10b9a022a695e26b291b9"},
{file = "cleo-0.7.6.tar.gz", hash = "sha256:99cf342406f3499cec43270fcfaf93c126c5164092eca201dfef0f623360b409"},
{file = "cleo-0.8.0-py2.py3-none-any.whl", hash = "sha256:dcb791b246c02d59ea8eaf05a05d986e547dbe8ba2496ed59048e5d4ab93b537"},
{file = "cleo-0.8.0.tar.gz", hash = "sha256:b2d56e93b182358591d0ec46c4f787736378a6a8adf4a6512f72aeb0506de981"},
]
click = [
{file = "Click-7.0-py2.py3-none-any.whl", hash = "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13"},
{file = "Click-7.0.tar.gz", hash = "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"},
]
clikit = [
{file = "clikit-0.4.3-py2.py3-none-any.whl", hash = "sha256:71e321b7795a2a6c4888629f43365d52db071737e668ab16861121d7dd3ada09"},
{file = "clikit-0.4.3.tar.gz", hash = "sha256:6e2d7e115e7c7b35bceb0209109935ab2f9ab50910e9ff2293f7fa0b7abf973e"},
{file = "clikit-0.5.1-py2.py3-none-any.whl", hash = "sha256:f697b6e3125cf4bdc6394184c687f52fe3323152ca6bb99db444201f32904a64"},
{file = "clikit-0.5.1.tar.gz", hash = "sha256:2e9cd4c87539d9a0f0275b26e3e50d2d531d7bfbcf4f91909aaa013cfac9d0e1"},
]
colorama = [
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
......@@ -1896,6 +1909,10 @@ webencodings = [
{file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
{file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
]
woops = [
{file = "woops-0.2.1-py3-none-any.whl", hash = "sha256:77f7179941d0a3d354d923f71428ee2c2519e0b86f465ed5882b98a6d00ec7f0"},
{file = "woops-0.2.1.tar.gz", hash = "sha256:f95dee22e055b61980209fdf481f33e743e02d5ef135853c9089d0061370f94a"},
]
zipp = [
{file = "zipp-1.2.0-py2.py3-none-any.whl", hash = "sha256:e0d9e63797e483a30d27e09fffd308c59a700d365ec34e93cc100844168bf921"},
{file = "zipp-1.2.0.tar.gz", hash = "sha256:c70410551488251b0fee67b460fb9a536af8d6f9f008ad10ac51f615b6a521b1"},
......
......@@ -12,6 +12,11 @@ class BuildCommand(EnvCommand):
option("format", "f", "Limit the format to either sdist or wheel.", flag=False)
]
loggers = [
"poetry.core.masonry.builders.sdist",
"poetry.core.masonry.builders.wheel",
]
def handle(self):
from poetry.core.masonry import Builder
......@@ -21,7 +26,7 @@ class BuildCommand(EnvCommand):
package = self.poetry.package
self.line(
"Building <c1>{}</c1> (<b>{}</b>)".format(
"Building <c1>{}</c1> (<c2>{}</c2>)".format(
package.pretty_name, package.version
)
)
......
......@@ -77,7 +77,7 @@ exist it will look for <comment>pyproject.toml</> and do the same.
return 0
self.line(
" - Installing <c1>{}</c1> (<b>{}</b>)".format(
" - Installing <c1>{}</c1> (<c2>{}</c2>)".format(
self.poetry.package.pretty_name, self.poetry.package.pretty_version
)
)
......
......@@ -36,11 +36,12 @@ class ApplicationConfig(BaseApplicationConfig):
super(ApplicationConfig, self).configure()
self.add_style(Style("c1").fg("cyan"))
self.add_style(Style("c2").fg("green"))
self.add_style(Style("info").fg("blue"))
self.add_style(Style("comment").fg("green"))
self.add_style(Style("error").fg("red").bold())
self.add_style(Style("warning").fg("yellow"))
self.add_style(Style("debug").fg("black").bold())
self.add_style(Style("warning").fg("yellow").bold())
self.add_style(Style("debug").fg("default").dark())
self.add_event_listener(PRE_HANDLE, self.register_command_loggers)
self.add_event_listener(PRE_HANDLE, self.set_env)
......
from .builder_formatter import BuilderLogFormatter
FORMATTERS = {
"poetry.core.masonry.builders.sdist": BuilderLogFormatter(),
"poetry.core.masonry.builders.wheel": BuilderLogFormatter(),
}
import re
from .formatter import Formatter
class BuilderLogFormatter(Formatter):
def format(self, msg): # type: (str) -> str
if msg.startswith(" - Building ") or msg.startswith(" - Built "):
msg = re.sub(r" - (Buil(?:t|ing)) (.+)", " - \\1 <c2>\\2</c2>", msg)
elif msg.startswith(" - Adding: "):
msg = re.sub(r" - Adding: (.+)", " - Adding: <b>\\1</b>", msg)
return msg
import logging
class Formatter(object):
def format(self, record): # type: (logging.LogRecord) -> str
raise NotImplementedError()
import logging
from .formatters import FORMATTERS
class IOFormatter(logging.Formatter):
......@@ -15,7 +17,9 @@ class IOFormatter(logging.Formatter):
level = record.levelname.lower()
msg = record.msg
if level in self._colors:
if record.name in FORMATTERS:
msg = FORMATTERS[record.name].format(msg)
elif level in self._colors:
msg = "<{}>{}</>".format(self._colors[level], msg)
return msg
......
......@@ -305,7 +305,7 @@ class Installer:
if operation.skipped:
if self.is_verbose() and (self._execute_operations or self.is_dry_run()):
self._io.write_line(
" - Skipping <c1>{}</c1> (<b>{}</b>) {}".format(
" - Skipping <c1>{}</c1> (<c2>{}</c2>) {}".format(
operation.package.pretty_name,
operation.package.full_pretty_version,
operation.skip_reason,
......@@ -316,7 +316,7 @@ class Installer:
if self._execute_operations or self.is_dry_run():
self._io.write_line(
" - Installing <c1>{}</c1> (<b>{}</b>)".format(
" - Installing <c1>{}</c1> (<c2>{}</c2>)".format(
operation.package.pretty_name, operation.package.full_pretty_version
)
)
......@@ -333,7 +333,7 @@ class Installer:
if operation.skipped:
if self.is_verbose() and (self._execute_operations or self.is_dry_run()):
self._io.write_line(
" - Skipping <c1>{}</c1> (<b>{}</b>) {}".format(
" - Skipping <c1>{}</c1> (<c2>{}</c2>) {}".format(
target.pretty_name,
target.full_pretty_version,
operation.skip_reason,
......@@ -344,7 +344,7 @@ class Installer:
if self._execute_operations or self.is_dry_run():
self._io.write_line(
" - Updating <c1>{}</c1> (<b>{}</b> -> <b>{}</b>)".format(
" - Updating <c1>{}</c1> (<c2>{}</c2> -> <c2>{}</c2>)".format(
target.pretty_name,
source.full_pretty_version,
target.full_pretty_version,
......@@ -360,7 +360,7 @@ class Installer:
if operation.skipped:
if self.is_verbose() and (self._execute_operations or self.is_dry_run()):
self._io.write_line(
" - Not removing <c1>{}</c1> (<b>{}</b>) {}".format(
" - Not removing <c1>{}</c1> (<c2>{}</c2>) {}".format(
operation.package.pretty_name,
operation.package.full_pretty_version,
operation.skip_reason,
......@@ -371,7 +371,7 @@ class Installer:
if self._execute_operations or self.is_dry_run():
self._io.write_line(
" - Removing <c1>{}</c1> (<b>{}</b>)".format(
" - Removing <c1>{}</c1> (<c2>{}</c2>)".format(
operation.package.pretty_name, operation.package.full_pretty_version
)
)
......
......@@ -29,7 +29,7 @@ class Publisher:
def publish(self, repository_name, username, password, cert=None, client_cert=None):
if repository_name:
self._io.write_line(
"Publishing <c1>{}</c1> (<b>{}</b>) "
"Publishing <c1>{}</c1> (<c2>{}</c2>) "
"to <info>{}</info>".format(
self._package.pretty_name,
self._package.pretty_version,
......@@ -38,7 +38,7 @@ class Publisher:
)
else:
self._io.write_line(
"Publishing <c1>{}</c1> (<b>{}</b>) "
"Publishing <c1>{}</c1> (<c2>{}</c2>) "
"to <info>PyPI</info>".format(
self._package.pretty_name, self._package.pretty_version
)
......
......@@ -731,31 +731,31 @@ class Provider:
message = (
"<fg=blue>fact</>: <c1>{}</c1>{} "
"depends on <c1>{}</c1> (<b>{}</b>)".format(
"depends on <c1>{}</c1> (<c2>{}</c2>)".format(
name, version, m.group(2), m.group(3)
)
)
elif " is " in message:
message = re.sub(
"fact: (.+) is (.+)",
"<fg=blue>fact</>: <c1>\\1</c1> is <b>\\2</b>",
"<fg=blue>fact</>: <c1>\\1</c1> is <c2>\\2</c2>",
message,
)
else:
message = re.sub(
r"(?<=: )(.+?) \((.+?)\)", "<c1>\\1</c1> (<b>\\2</b>)", message
r"(?<=: )(.+?) \((.+?)\)", "<c1>\\1</c1> (<c2>\\2</c2>)", message
)
message = "<fg=blue>fact</>: {}".format(message.split("fact: ")[1])
elif message.startswith("selecting "):
message = re.sub(
r"selecting (.+?) \((.+?)\)",
"<fg=blue>selecting</> <c1>\\1</c1> (<b>\\2</b>)",
"<fg=blue>selecting</> <c1>\\1</c1> (<c2>\\2</c2>)",
message,
)
elif message.startswith("derived:"):
m = re.match(r"derived: (.+?) \((.+?)\)$", message)
if m:
message = "<fg=blue>derived</>: <c1>{}</c1> (<b>{}</b>)".format(
message = "<fg=blue>derived</>: <c1>{}</c1> (<c2>{}</c2>)".format(
m.group(1), m.group(2)
)
else:
......@@ -768,14 +768,14 @@ class Provider:
m2 = re.match(r"(.+?) \((.+?)\)", m.group(1))
if m2:
name = m2.group(1)
version = " (<b>{}</b>)".format(m2.group(2))
version = " (<c2>{}</c2>)".format(m2.group(2))
else:
name = m.group(1)
version = ""
message = (
"<fg=red;options=bold>conflict</>: <c1>{}</c1>{} "
"depends on <c1>{}</c1> (<b>{}</b>)".format(
"depends on <c1>{}</c1> (<c2>{}</c2>)".format(
name, version, m.group(2), m.group(3)
)
)
......@@ -791,7 +791,7 @@ class Provider:
debug_info = (
"\n".join(
[
"<comment>{}:</> {}".format(str(depth).rjust(4), s)
"<debug>{}:</debug> {}".format(str(depth).rjust(4), s)
for s in debug_info.split("\n")
]
)
......@@ -806,9 +806,7 @@ class Provider:
self._io.write_line("Resolving dependencies...")
yield
else:
indicator = Indicator(
self._io, "{message} <fg=black;options=bold>({elapsed:2s})</>"
)
indicator = Indicator(self._io, "{message} <debug>({elapsed:2s})</debug>")
with indicator.auto(
"<info>Resolving dependencies...</info>",
......
......@@ -503,4 +503,4 @@ class PyPiRepository(RemoteRepository):
f.write(chunk)
def _log(self, msg, level="info"):
getattr(logger, level)("<comment>{}:</comment> {}".format(self._name, msg))
getattr(logger, level)("<debug>{}:</debug> {}".format(self._name, msg))
......@@ -24,8 +24,8 @@ classifiers = [
[tool.poetry.dependencies]
python = "~2.7 || ^3.5"
poetry-core = "^1.0.0a5"
cleo = "^0.7.6"
clikit = "^0.4.3"
cleo = "^0.8.0"
clikit = "^0.5.1"
requests = "^2.18"
cachy = "^0.3.0"
requests-toolbelt = "^0.8.0"
......
import pytest
from poetry.utils._compat import PY36
from poetry.utils._compat import Path
@pytest.mark.skipif(
not PY36, reason="Improved error rendering is only available on Python >=3.6"
)
def test_publish_returns_non_zero_code_for_upload_errors(app, app_tester, http):
http.register_uri(
http.POST, "https://upload.pypi.org/legacy/", status=400, body="Bad Request"
......@@ -14,7 +20,34 @@ def test_publish_returns_non_zero_code_for_upload_errors(app, app_tester, http):
Publishing simple-project (1.2.3) to PyPI
[UploadError]
UploadError
HTTP Error 400: Bad Request
"""
assert expected in app_tester.io.fetch_output()
@pytest.mark.skipif(
PY36, reason="Improved error rendering is not available on Python <3.6"
)
def test_publish_returns_non_zero_code_for_upload_errors_older_python(
app, app_tester, http
):
http.register_uri(
http.POST, "https://upload.pypi.org/legacy/", status=400, body="Bad Request"
)
exit_code = app_tester.execute("publish --username foo --password bar")
assert 1 == exit_code
expected = """
Publishing simple-project (1.2.3) to PyPI
UploadError
HTTP Error 400: Bad Request
"""
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment