diff --git a/poetry.lock b/poetry.lock index 2bd0b2f81ae..36fd2d14110 100644 --- a/poetry.lock +++ b/poetry.lock @@ -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." @@ -1303,6 +1307,15 @@ optional = false python-versions = "*" 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" @@ -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"}, diff --git a/poetry/console/commands/build.py b/poetry/console/commands/build.py index 3d450fb3194..d2473ab0bfd 100644 --- a/poetry/console/commands/build.py +++ b/poetry/console/commands/build.py @@ -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 @@ def handle(self): package = self.poetry.package self.line( - "Building {} ({})".format( + "Building {} ({})".format( package.pretty_name, package.version ) ) diff --git a/poetry/console/commands/install.py b/poetry/console/commands/install.py index 82585afe96f..8a820c3d585 100644 --- a/poetry/console/commands/install.py +++ b/poetry/console/commands/install.py @@ -77,7 +77,7 @@ def handle(self): return 0 self.line( - " - Installing {} ({})".format( + " - Installing {} ({})".format( self.poetry.package.pretty_name, self.poetry.package.pretty_version ) ) diff --git a/poetry/console/config/application_config.py b/poetry/console/config/application_config.py index 97570956b09..e8f752752fd 100644 --- a/poetry/console/config/application_config.py +++ b/poetry/console/config/application_config.py @@ -36,11 +36,12 @@ def configure(self): 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) diff --git a/poetry/console/logging/formatters/__init__.py b/poetry/console/logging/formatters/__init__.py new file mode 100644 index 00000000000..247f51bec8e --- /dev/null +++ b/poetry/console/logging/formatters/__init__.py @@ -0,0 +1,7 @@ +from .builder_formatter import BuilderLogFormatter + + +FORMATTERS = { + "poetry.core.masonry.builders.sdist": BuilderLogFormatter(), + "poetry.core.masonry.builders.wheel": BuilderLogFormatter(), +} diff --git a/poetry/console/logging/formatters/builder_formatter.py b/poetry/console/logging/formatters/builder_formatter.py new file mode 100644 index 00000000000..df2f7e301fa --- /dev/null +++ b/poetry/console/logging/formatters/builder_formatter.py @@ -0,0 +1,13 @@ +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 \\2", msg) + elif msg.startswith(" - Adding: "): + msg = re.sub(r" - Adding: (.+)", " - Adding: \\1", msg) + + return msg diff --git a/poetry/console/logging/formatters/formatter.py b/poetry/console/logging/formatters/formatter.py new file mode 100644 index 00000000000..35b59374be4 --- /dev/null +++ b/poetry/console/logging/formatters/formatter.py @@ -0,0 +1,6 @@ +import logging + + +class Formatter(object): + def format(self, record): # type: (logging.LogRecord) -> str + raise NotImplementedError() diff --git a/poetry/console/logging/io_formatter.py b/poetry/console/logging/io_formatter.py index c9e389a3e4b..9ff57fec761 100644 --- a/poetry/console/logging/io_formatter.py +++ b/poetry/console/logging/io_formatter.py @@ -1,5 +1,7 @@ import logging +from .formatters import FORMATTERS + class IOFormatter(logging.Formatter): @@ -15,7 +17,9 @@ def format(self, record): 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 diff --git a/poetry/installation/installer.py b/poetry/installation/installer.py index f506e111949..66cb03373fb 100644 --- a/poetry/installation/installer.py +++ b/poetry/installation/installer.py @@ -305,7 +305,7 @@ def _execute_install(self, operation): # type: (Install) -> None if operation.skipped: if self.is_verbose() and (self._execute_operations or self.is_dry_run()): self._io.write_line( - " - Skipping {} ({}) {}".format( + " - Skipping {} ({}) {}".format( operation.package.pretty_name, operation.package.full_pretty_version, operation.skip_reason, @@ -316,7 +316,7 @@ def _execute_install(self, operation): # type: (Install) -> None if self._execute_operations or self.is_dry_run(): self._io.write_line( - " - Installing {} ({})".format( + " - Installing {} ({})".format( operation.package.pretty_name, operation.package.full_pretty_version ) ) @@ -333,7 +333,7 @@ def _execute_update(self, operation): # type: (Update) -> None if operation.skipped: if self.is_verbose() and (self._execute_operations or self.is_dry_run()): self._io.write_line( - " - Skipping {} ({}) {}".format( + " - Skipping {} ({}) {}".format( target.pretty_name, target.full_pretty_version, operation.skip_reason, @@ -344,7 +344,7 @@ def _execute_update(self, operation): # type: (Update) -> None if self._execute_operations or self.is_dry_run(): self._io.write_line( - " - Updating {} ({} -> {})".format( + " - Updating {} ({} -> {})".format( target.pretty_name, source.full_pretty_version, target.full_pretty_version, @@ -360,7 +360,7 @@ def _execute_uninstall(self, operation): # type: (Uninstall) -> None if operation.skipped: if self.is_verbose() and (self._execute_operations or self.is_dry_run()): self._io.write_line( - " - Not removing {} ({}) {}".format( + " - Not removing {} ({}) {}".format( operation.package.pretty_name, operation.package.full_pretty_version, operation.skip_reason, @@ -371,7 +371,7 @@ def _execute_uninstall(self, operation): # type: (Uninstall) -> None if self._execute_operations or self.is_dry_run(): self._io.write_line( - " - Removing {} ({})".format( + " - Removing {} ({})".format( operation.package.pretty_name, operation.package.full_pretty_version ) ) diff --git a/poetry/publishing/publisher.py b/poetry/publishing/publisher.py index b62b3addbe6..f1972d78eb7 100644 --- a/poetry/publishing/publisher.py +++ b/poetry/publishing/publisher.py @@ -29,7 +29,7 @@ def files(self): def publish(self, repository_name, username, password, cert=None, client_cert=None): if repository_name: self._io.write_line( - "Publishing {} ({}) " + "Publishing {} ({}) " "to {}".format( self._package.pretty_name, self._package.pretty_version, @@ -38,7 +38,7 @@ def publish(self, repository_name, username, password, cert=None, client_cert=No ) else: self._io.write_line( - "Publishing {} ({}) " + "Publishing {} ({}) " "to PyPI".format( self._package.pretty_name, self._package.pretty_version ) diff --git a/poetry/puzzle/provider.py b/poetry/puzzle/provider.py index fa87f2ef09f..ea42a088c95 100644 --- a/poetry/puzzle/provider.py +++ b/poetry/puzzle/provider.py @@ -731,31 +731,31 @@ def debug(self, message, depth=0): message = ( "fact: {}{} " - "depends on {} ({})".format( + "depends on {} ({})".format( name, version, m.group(2), m.group(3) ) ) elif " is " in message: message = re.sub( "fact: (.+) is (.+)", - "fact: \\1 is \\2", + "fact: \\1 is \\2", message, ) else: message = re.sub( - r"(?<=: )(.+?) \((.+?)\)", "\\1 (\\2)", message + r"(?<=: )(.+?) \((.+?)\)", "\\1 (\\2)", message ) message = "fact: {}".format(message.split("fact: ")[1]) elif message.startswith("selecting "): message = re.sub( r"selecting (.+?) \((.+?)\)", - "selecting \\1 (\\2)", + "selecting \\1 (\\2)", message, ) elif message.startswith("derived:"): m = re.match(r"derived: (.+?) \((.+?)\)$", message) if m: - message = "derived: {} ({})".format( + message = "derived: {} ({})".format( m.group(1), m.group(2) ) else: @@ -768,14 +768,14 @@ def debug(self, message, depth=0): m2 = re.match(r"(.+?) \((.+?)\)", m.group(1)) if m2: name = m2.group(1) - version = " ({})".format(m2.group(2)) + version = " ({})".format(m2.group(2)) else: name = m.group(1) version = "" message = ( "conflict: {}{} " - "depends on {} ({})".format( + "depends on {} ({})".format( name, version, m.group(2), m.group(3) ) ) @@ -791,7 +791,7 @@ def debug(self, message, depth=0): debug_info = ( "\n".join( [ - "{}: {}".format(str(depth).rjust(4), s) + "{}: {}".format(str(depth).rjust(4), s) for s in debug_info.split("\n") ] ) @@ -806,9 +806,7 @@ def progress(self): self._io.write_line("Resolving dependencies...") yield else: - indicator = Indicator( - self._io, "{message} ({elapsed:2s})" - ) + indicator = Indicator(self._io, "{message} ({elapsed:2s})") with indicator.auto( "Resolving dependencies...", diff --git a/poetry/repositories/pypi_repository.py b/poetry/repositories/pypi_repository.py index 76b37daf5bb..e110da77879 100644 --- a/poetry/repositories/pypi_repository.py +++ b/poetry/repositories/pypi_repository.py @@ -503,4 +503,4 @@ def _download(self, url, dest): # type: (str, str) -> None f.write(chunk) def _log(self, msg, level="info"): - getattr(logger, level)("{}: {}".format(self._name, msg)) + getattr(logger, level)("{}: {}".format(self._name, msg)) diff --git a/pyproject.toml b/pyproject.toml index 2b1940a3f65..2fc42001536 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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" diff --git a/tests/console/commands/test_publish.py b/tests/console/commands/test_publish.py index 5415694fa3c..0d9b655dd9e 100644 --- a/tests/console/commands/test_publish.py +++ b/tests/console/commands/test_publish.py @@ -1,6 +1,12 @@ +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 """