From 14bc3c40099a33a692b2f6ce0a0626698905d8fe Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 3 Dec 2015 20:07:18 -0200 Subject: [PATCH] Fix pastebin when captured output contains non-ascii characters Fix #1219 --- CHANGELOG | 3 +++ _pytest/pastebin.py | 14 +++++++++----- testing/test_pastebin.py | 35 +++++++++++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e335e7a03..9e8d104b9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,9 @@ - fix #1198: ``--pastebin`` option now works on Python 3. Thanks Mehdy Khoshnoody for the PR. +- fix #1219: ``--pastebin`` now works correctly when captured output contains + non-ascii characters. Thanks Bruno Oliveira for the PR. + - fix #1204: another error when collecting with a nasty __getattr__(). Thanks Florian Bruhin for the PR. diff --git a/_pytest/pastebin.py b/_pytest/pastebin.py index 20738134e..4ec62d022 100644 --- a/_pytest/pastebin.py +++ b/_pytest/pastebin.py @@ -13,17 +13,21 @@ def pytest_addoption(parser): @pytest.hookimpl(trylast=True) def pytest_configure(config): + import py if config.option.pastebin == "all": tr = config.pluginmanager.getplugin('terminalreporter') # if no terminal reporter plugin is present, nothing we can do here; # this can happen when this function executes in a slave node # when using pytest-xdist, for example if tr is not None: - config._pastebinfile = tempfile.TemporaryFile('w+') + # pastebin file will be utf-8 encoded binary file + config._pastebinfile = tempfile.TemporaryFile('w+b') oldwrite = tr._tw.write def tee_write(s, **kwargs): oldwrite(s, **kwargs) - config._pastebinfile.write(str(s)) + if py.builtin._istext(s): + s = s.encode('utf-8') + config._pastebinfile.write(s) tr._tw.write = tee_write def pytest_unconfigure(config): @@ -45,7 +49,7 @@ def create_new_paste(contents): """ Creates a new paste using bpaste.net service. - :contents: paste contents + :contents: paste contents as utf-8 encoded bytes :returns: url to the pasted contents """ import re @@ -61,8 +65,8 @@ def create_new_paste(contents): 'expiry': '1week', } url = 'https://bpaste.net' - response = urlopen(url, data=urlencode(params).encode()).read() - m = re.search(r'href="/raw/(\w+)"', response.decode()) + response = urlopen(url, data=urlencode(params).encode('ascii')).read() + m = re.search(r'href="/raw/(\w+)"', response.decode('utf-8')) if m: return '%s/show/%s' % (url, m.group(1)) else: diff --git a/testing/test_pastebin.py b/testing/test_pastebin.py index 2fe4bc137..03570a5c7 100644 --- a/testing/test_pastebin.py +++ b/testing/test_pastebin.py @@ -1,3 +1,4 @@ +# encoding: utf-8 import sys import pytest @@ -27,6 +28,7 @@ class TestPasteCapture: assert reprec.countoutcomes() == [1,1,1] def test_all(self, testdir, pastebinlist): + from _pytest.pytester import LineMatcher testpath = testdir.makepyfile(""" import pytest def test_pass(): @@ -39,9 +41,34 @@ class TestPasteCapture: reprec = testdir.inline_run(testpath, "--pastebin=all", '-v') assert reprec.countoutcomes() == [1,1,1] assert len(pastebinlist) == 1 - s = pastebinlist[0] - for x in 'test_fail test_skip test_pass'.split(): - assert x in s + contents = pastebinlist[0].decode('utf-8') + matcher = LineMatcher(contents.splitlines()) + matcher.fnmatch_lines([ + '*test_pass PASSED*', + '*test_fail FAILED*', + '*test_skip SKIPPED*', + '*== 1 failed, 1 passed, 1 skipped in *' + ]) + + def test_non_ascii_paste_text(self, testdir): + """Make sure that text which contains non-ascii characters is pasted + correctly. See #1219. + """ + testdir.makepyfile(test_unicode=""" + # encoding: utf-8 + def test(): + assert '☺' == 1 + """) + result = testdir.runpytest('--pastebin=all') + if sys.version_info[0] == 3: + expected_msg = "*assert '☺' == 1*" + else: + expected_msg = "*assert '\\xe2\\x98\\xba' == 1*" + result.stdout.fnmatch_lines([ + expected_msg, + "*== 1 failed in *", + '*Sending information to Paste Service*', + ]) class TestPaste: @@ -74,7 +101,7 @@ class TestPaste: return calls def test_create_new_paste(self, pastebin, mocked_urlopen): - result = pastebin.create_new_paste('full-paste-contents') + result = pastebin.create_new_paste(b'full-paste-contents') assert result == 'https://bpaste.net/show/3c0c6750bd' assert len(mocked_urlopen) == 1 url, data = mocked_urlopen[0]