From 6c2987d5097122fa3302cea7dcf064672c92deb6 Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Mon, 18 Jun 2018 13:22:24 -0500 Subject: [PATCH 01/13] BUG: Except IOErrors when attempting to retrieve default client encoding. instead of a more specific AttributeError to include the thrown IOError. --- pandas/io/formats/console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/io/formats/console.py b/pandas/io/formats/console.py index 36eac8dd57fbd..8ab654294d29b 100644 --- a/pandas/io/formats/console.py +++ b/pandas/io/formats/console.py @@ -21,7 +21,7 @@ def detect_console_encoding(): encoding = None try: encoding = sys.stdout.encoding or sys.stdin.encoding - except AttributeError: + except Exception: pass # try again for something better From c31b9144dcd0807b35ac1859d7d609e8d74ce6cf Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Tue, 19 Jun 2018 22:19:23 -0500 Subject: [PATCH 02/13] BUG: Allow IOErrors when attempting to retrieve client encoding. (#21552) * When using mod_wsgi, access to sys.stdout is restricted by default. To handle this case, catch IOErrors alongside the already handled AttributeErrors. --- doc/source/whatsnew/v0.23.2.txt | 2 +- pandas/io/formats/console.py | 2 +- pandas/tests/io/formats/test_console.py | 34 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 pandas/tests/io/formats/test_console.py diff --git a/doc/source/whatsnew/v0.23.2.txt b/doc/source/whatsnew/v0.23.2.txt index b8d865195cddd..dab349f6953e0 100644 --- a/doc/source/whatsnew/v0.23.2.txt +++ b/doc/source/whatsnew/v0.23.2.txt @@ -59,7 +59,7 @@ Bug Fixes **I/O** -- +- Bug in :meth:`detect_client_encoding` where potential IOError goes unhandled when importing in a mod_wsgi process due to restricted access to stdout. (:issue:`21552`) - **Plotting** diff --git a/pandas/io/formats/console.py b/pandas/io/formats/console.py index 36eac8dd57fbd..8ff9f6403a0ac 100644 --- a/pandas/io/formats/console.py +++ b/pandas/io/formats/console.py @@ -21,7 +21,7 @@ def detect_console_encoding(): encoding = None try: encoding = sys.stdout.encoding or sys.stdin.encoding - except AttributeError: + except (AttributeError, IOError): pass # try again for something better diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py new file mode 100644 index 0000000000000..9a0c47a2a9616 --- /dev/null +++ b/pandas/tests/io/formats/test_console.py @@ -0,0 +1,34 @@ +import pytest +from functools import partial + +from pandas.io.formats.console import detect_console_encoding, locale + + +def mock_raises_exception(error=Exception): + raise error + + +def test_detect_console_encoding_stdout(monkeypatch): + monkeypatch.setattr('sys.stdin.encoding', '') + monkeypatch.setattr('sys.stdout.encoding', 'foo') + assert detect_console_encoding() == 'foo' + + +def test_detect_console_encoding_stdin(monkeypatch): + monkeypatch.setattr('sys.stdout.encoding', '') + monkeypatch.setattr('sys.stdin.encoding', 'foo') + assert detect_console_encoding() == 'foo' + + +@pytest.mark.parametrize('error', [AttributeError, IOError]) +def test_detect_console_encoding_stdout_error_uses_locale(monkeypatch, error): + monkeypatch.setattr('locale.getpreferredencoding', lambda: 'foo') + monkeypatch.setattr('sys.stdout', partial(mock_raises_exception, error)) + assert detect_console_encoding() == 'foo' + + +def test_detect_console_encoding_sys_default_encoding(monkeypatch): + monkeypatch.setattr('locale.getpreferredencoding', mock_raises_exception) + monkeypatch.setattr('sys.stdout', mock_raises_exception) + monkeypatch.setattr('sys.getdefaultencoding', lambda: 'sysDefaultEncoding') + assert detect_console_encoding() == 'sysDefaultEncoding' From b37cb752073c2255edfd7f6220ae1c78f2cab466 Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Wed, 20 Jun 2018 08:33:12 -0500 Subject: [PATCH 03/13] Fixup tests to add more scenarios. Code review changes. --- doc/source/whatsnew/v0.23.2.txt | 2 +- pandas/tests/io/formats/test_console.py | 49 +++++++++++++++---------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/doc/source/whatsnew/v0.23.2.txt b/doc/source/whatsnew/v0.23.2.txt index dab349f6953e0..84e1adb8db74f 100644 --- a/doc/source/whatsnew/v0.23.2.txt +++ b/doc/source/whatsnew/v0.23.2.txt @@ -59,7 +59,7 @@ Bug Fixes **I/O** -- Bug in :meth:`detect_client_encoding` where potential IOError goes unhandled when importing in a mod_wsgi process due to restricted access to stdout. (:issue:`21552`) +- Bug in :meth:`detect_client_encoding` where potential ``IOError`` goes unhandled when importing in a mod_wsgi process due to restricted access to stdout. (:issue:`21552`) - **Plotting** diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index 9a0c47a2a9616..d7e0974c8cb96 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -7,28 +7,39 @@ def mock_raises_exception(error=Exception): raise error - -def test_detect_console_encoding_stdout(monkeypatch): - monkeypatch.setattr('sys.stdin.encoding', '') - monkeypatch.setattr('sys.stdout.encoding', 'foo') - assert detect_console_encoding() == 'foo' - - -def test_detect_console_encoding_stdin(monkeypatch): - monkeypatch.setattr('sys.stdout.encoding', '') - monkeypatch.setattr('sys.stdin.encoding', 'foo') - assert detect_console_encoding() == 'foo' - - -@pytest.mark.parametrize('error', [AttributeError, IOError]) -def test_detect_console_encoding_stdout_error_uses_locale(monkeypatch, error): +@pytest.mark.parametrize('empty,filled', [ + ['stdin', 'stdout'], + ['stdout', 'stdin'] +]) +def test_detect_console_encoding_from_stdout_stdin(monkeypatch, empty, filled): + # Ensure that when sys.stdout.encoding or sys.stdin.encoding is used when + # they have values filled. + monkeypatch.setattr('sys.{}.encoding'.format(empty), '') + monkeypatch.setattr('sys.{}.encoding'.format(filled), filled) + assert detect_console_encoding() == filled + + +@pytest.mark.parametrize('stdEncoding', [ + partial(mock_raises_exception, AttributeError), + partial(mock_raises_exception, IOError), + lambda: 'ascii' +]) +def test_detect_console_encoding_fallback_to_locale(monkeypatch, stdEncoding): monkeypatch.setattr('locale.getpreferredencoding', lambda: 'foo') - monkeypatch.setattr('sys.stdout', partial(mock_raises_exception, error)) + monkeypatch.setattr('sys.stdout', stdEncoding) assert detect_console_encoding() == 'foo' -def test_detect_console_encoding_sys_default_encoding(monkeypatch): - monkeypatch.setattr('locale.getpreferredencoding', mock_raises_exception) - monkeypatch.setattr('sys.stdout', mock_raises_exception) +@pytest.mark.parametrize('std,locale', [ + ['ascii', 'ascii'], + ['ascii', mock_raises_exception], + [mock_raises_exception, 'ascii'], + [mock_raises_exception, mock_raises_exception] +]) +def test_detect_console_encoding_fallback_to_default(monkeypatch, std, locale): + # When both the stdout/stdin encoding and locale preferred encoding checks + # fail (or return 'ascii', we should default to the sys default encoding. + monkeypatch.setattr('sys.stdout', std) + monkeypatch.setattr('locale.getpreferredencoding', locale) monkeypatch.setattr('sys.getdefaultencoding', lambda: 'sysDefaultEncoding') assert detect_console_encoding() == 'sysDefaultEncoding' From bf26adb9cbe2f8b79e11d076b5172e86abb5df69 Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Wed, 20 Jun 2018 08:37:27 -0500 Subject: [PATCH 04/13] PEP8 changes. --- pandas/tests/io/formats/test_console.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index d7e0974c8cb96..f8bf4bb24415b 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -7,6 +7,7 @@ def mock_raises_exception(error=Exception): raise error + @pytest.mark.parametrize('empty,filled', [ ['stdin', 'stdout'], ['stdout', 'stdin'] From 77a95d247f50015303d823108c2874c942b0f860 Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Mon, 25 Jun 2018 20:11:20 -0500 Subject: [PATCH 05/13] Mock out stdout/stdin instead of trying to access their properties. --- pandas/tests/io/formats/test_console.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index f8bf4bb24415b..5bca53e18ad49 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -13,10 +13,15 @@ def mock_raises_exception(error=Exception): ['stdout', 'stdin'] ]) def test_detect_console_encoding_from_stdout_stdin(monkeypatch, empty, filled): - # Ensure that when sys.stdout.encoding or sys.stdin.encoding is used when + # Ensures that when sys.stdout.encoding or sys.stdin.encoding is used when # they have values filled. - monkeypatch.setattr('sys.{}.encoding'.format(empty), '') - monkeypatch.setattr('sys.{}.encoding'.format(filled), filled) + class MockEncoding(object): + def __init__(self, encoding): + super(MockEncoding, self).__init__() + self.encoding = encoding + + monkeypatch.setattr('sys.{}'.format(empty), MockEncoding('')) + monkeypatch.setattr('sys.{}'.format(filled), MockEncoding(filled)) assert detect_console_encoding() == filled From 59e0993dbeeb0a200e0b87e14a1ee36aa11d634a Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Thu, 12 Jul 2018 21:57:07 -0500 Subject: [PATCH 06/13] Use context manager when monkeypatching standard library items. --- pandas/tests/io/formats/test_console.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index 5bca53e18ad49..094786afb1241 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -20,9 +20,10 @@ def __init__(self, encoding): super(MockEncoding, self).__init__() self.encoding = encoding - monkeypatch.setattr('sys.{}'.format(empty), MockEncoding('')) - monkeypatch.setattr('sys.{}'.format(filled), MockEncoding(filled)) - assert detect_console_encoding() == filled + with monkeypatch.context() as context: + context.setattr('sys.{}'.format(empty), MockEncoding('')) + context.setattr('sys.{}'.format(filled), MockEncoding(filled)) + assert detect_console_encoding() == filled @pytest.mark.parametrize('stdEncoding', [ @@ -32,8 +33,9 @@ def __init__(self, encoding): ]) def test_detect_console_encoding_fallback_to_locale(monkeypatch, stdEncoding): monkeypatch.setattr('locale.getpreferredencoding', lambda: 'foo') - monkeypatch.setattr('sys.stdout', stdEncoding) - assert detect_console_encoding() == 'foo' + with monkeypatch.context() as context: + context.setattr('sys.stdout', stdEncoding) + assert detect_console_encoding() == 'foo' @pytest.mark.parametrize('std,locale', [ @@ -45,7 +47,8 @@ def test_detect_console_encoding_fallback_to_locale(monkeypatch, stdEncoding): def test_detect_console_encoding_fallback_to_default(monkeypatch, std, locale): # When both the stdout/stdin encoding and locale preferred encoding checks # fail (or return 'ascii', we should default to the sys default encoding. - monkeypatch.setattr('sys.stdout', std) monkeypatch.setattr('locale.getpreferredencoding', locale) - monkeypatch.setattr('sys.getdefaultencoding', lambda: 'sysDefaultEncoding') - assert detect_console_encoding() == 'sysDefaultEncoding' + with monkeypatch.context() as context: + context.setattr('sys.stdout', std) + context.setattr('sys.getdefaultencoding', lambda: 'sysDefaultEncoding') + assert detect_console_encoding() == 'sysDefaultEncoding' From 4790e10792d3fb14a4eb6590f91f7fcf117b3277 Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Sat, 14 Jul 2018 13:01:00 -0500 Subject: [PATCH 07/13] Fix linter erros/code review changes. --- doc/source/whatsnew/v0.23.4.txt | 2 +- pandas/tests/io/formats/test_console.py | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v0.23.4.txt b/doc/source/whatsnew/v0.23.4.txt index a88c22e3d01f7..fe7b0981273d6 100644 --- a/doc/source/whatsnew/v0.23.4.txt +++ b/doc/source/whatsnew/v0.23.4.txt @@ -41,7 +41,7 @@ Bug Fixes **I/O** -- +- Bug in :meth:`detect_client_encoding` where potential ``IOError`` goes unhandled when importing in a mod_wsgi process due to restricted access to stdout. (:issue:`21552`) - **Categorical** diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index 094786afb1241..8e9a2e433d3cc 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -1,7 +1,7 @@ -import pytest from functools import partial +import pytest -from pandas.io.formats.console import detect_console_encoding, locale +from pandas.io.formats.console import detect_console_encoding def mock_raises_exception(error=Exception): @@ -15,6 +15,7 @@ def mock_raises_exception(error=Exception): def test_detect_console_encoding_from_stdout_stdin(monkeypatch, empty, filled): # Ensures that when sys.stdout.encoding or sys.stdin.encoding is used when # they have values filled. + # GH 21552 class MockEncoding(object): def __init__(self, encoding): super(MockEncoding, self).__init__() @@ -32,8 +33,9 @@ def __init__(self, encoding): lambda: 'ascii' ]) def test_detect_console_encoding_fallback_to_locale(monkeypatch, stdEncoding): - monkeypatch.setattr('locale.getpreferredencoding', lambda: 'foo') + # GH 21552 with monkeypatch.context() as context: + context.setattr('locale.getpreferredencoding', lambda: 'foo') context.setattr('sys.stdout', stdEncoding) assert detect_console_encoding() == 'foo' @@ -47,8 +49,9 @@ def test_detect_console_encoding_fallback_to_locale(monkeypatch, stdEncoding): def test_detect_console_encoding_fallback_to_default(monkeypatch, std, locale): # When both the stdout/stdin encoding and locale preferred encoding checks # fail (or return 'ascii', we should default to the sys default encoding. - monkeypatch.setattr('locale.getpreferredencoding', locale) + # GH 21552 with monkeypatch.context() as context: + context.setattr('locale.getpreferredencoding', locale) context.setattr('sys.stdout', std) context.setattr('sys.getdefaultencoding', lambda: 'sysDefaultEncoding') assert detect_console_encoding() == 'sysDefaultEncoding' From 61568255638f421e0ae1ddf7db9592d6f296c45c Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Sun, 15 Jul 2018 22:07:45 -0500 Subject: [PATCH 08/13] Move fix to 0.24.0 release; simplify tests. --- doc/source/whatsnew/v0.23.2.txt | 2 -- doc/source/whatsnew/v0.23.4.txt | 2 +- doc/source/whatsnew/v0.24.0.txt | 2 +- pandas/tests/io/formats/test_console.py | 15 +++++---------- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/doc/source/whatsnew/v0.23.2.txt b/doc/source/whatsnew/v0.23.2.txt index c970dbfb9a344..3f68eabdca4c2 100644 --- a/doc/source/whatsnew/v0.23.2.txt +++ b/doc/source/whatsnew/v0.23.2.txt @@ -89,8 +89,6 @@ Bug Fixes - Bug in :func:`read_csv` that caused it to incorrectly raise an error when ``nrows=0``, ``low_memory=True``, and ``index_col`` was not ``None`` (:issue:`21141`) - Bug in :func:`json_normalize` when formatting the ``record_prefix`` with integer columns (:issue:`21536`) -- Bug in :meth:`detect_client_encoding` where potential ``IOError`` goes unhandled when importing in a mod_wsgi process due to restricted access to stdout. (:issue:`21552`) -- **Categorical** diff --git a/doc/source/whatsnew/v0.23.4.txt b/doc/source/whatsnew/v0.23.4.txt index fe7b0981273d6..a88c22e3d01f7 100644 --- a/doc/source/whatsnew/v0.23.4.txt +++ b/doc/source/whatsnew/v0.23.4.txt @@ -41,7 +41,7 @@ Bug Fixes **I/O** -- Bug in :meth:`detect_client_encoding` where potential ``IOError`` goes unhandled when importing in a mod_wsgi process due to restricted access to stdout. (:issue:`21552`) +- - **Categorical** diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index ed4022d422b4d..fbfd80d4334bc 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -384,7 +384,7 @@ I/O - :func:`read_html()` no longer ignores all-whitespace ```` within ```` when considering the ``skiprows`` and ``header`` arguments. Previously, users had to decrease their ``header`` and ``skiprows`` values on such tables to work around the issue. (:issue:`21641`) - :func:`read_excel()` will correctly show the deprecation warning for previously deprecated ``sheetname`` (:issue:`17994`) -- +- Bug in :meth:`detect_client_encoding` where potential ``IOError`` goes unhandled when importing in a mod_wsgi process due to restricted access to stdout. (:issue:`21552`) Plotting ^^^^^^^^ diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index 8e9a2e433d3cc..6118134930aee 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -1,13 +1,8 @@ -from functools import partial import pytest from pandas.io.formats.console import detect_console_encoding -def mock_raises_exception(error=Exception): - raise error - - @pytest.mark.parametrize('empty,filled', [ ['stdin', 'stdout'], ['stdout', 'stdin'] @@ -28,8 +23,8 @@ def __init__(self, encoding): @pytest.mark.parametrize('stdEncoding', [ - partial(mock_raises_exception, AttributeError), - partial(mock_raises_exception, IOError), + pytest.raises(AttributeError), + pytest.raises(IOError), lambda: 'ascii' ]) def test_detect_console_encoding_fallback_to_locale(monkeypatch, stdEncoding): @@ -42,9 +37,9 @@ def test_detect_console_encoding_fallback_to_locale(monkeypatch, stdEncoding): @pytest.mark.parametrize('std,locale', [ ['ascii', 'ascii'], - ['ascii', mock_raises_exception], - [mock_raises_exception, 'ascii'], - [mock_raises_exception, mock_raises_exception] + ['ascii', pytest.raises(Exception)], + [pytest.raises(Exception), 'ascii'], + [pytest.raises(Exception), pytest.raises(Exception)] ]) def test_detect_console_encoding_fallback_to_default(monkeypatch, std, locale): # When both the stdout/stdin encoding and locale preferred encoding checks From fb1dc2eb477510a2323f531ef0ffc6beab4aca81 Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Mon, 23 Jul 2018 22:43:37 -0500 Subject: [PATCH 09/13] Attempt at trying to get side_effects right without using mock. * pytest.raises RaisesContext objects were throwing their own attribute errors which were being successfully caught and handled when they shouldn't have been. This adds a helper MockEncoding object which lets us either return or raise when the encoding object is accessed. --- pandas/tests/io/formats/test_console.py | 39 +++++++++++++++++-------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index 6118134930aee..15b2e32cc7740 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -3,6 +3,24 @@ from pandas.io.formats.console import detect_console_encoding +class MockEncoding(object): + """ + Used to add a side effect when accessing the 'encoding' property. If the + side effect is a str in nature, the value will be returned. Otherwise, the + side effect should be an exception that will be raised. + """ + def __init__(self, encoding): + super(MockEncoding, self).__init__() + self.val = encoding + + @property + def encoding(self): + if isinstance(self.val, str): + return self.val + else: + raise self.val + + @pytest.mark.parametrize('empty,filled', [ ['stdin', 'stdout'], ['stdout', 'stdin'] @@ -11,11 +29,6 @@ def test_detect_console_encoding_from_stdout_stdin(monkeypatch, empty, filled): # Ensures that when sys.stdout.encoding or sys.stdin.encoding is used when # they have values filled. # GH 21552 - class MockEncoding(object): - def __init__(self, encoding): - super(MockEncoding, self).__init__() - self.encoding = encoding - with monkeypatch.context() as context: context.setattr('sys.{}'.format(empty), MockEncoding('')) context.setattr('sys.{}'.format(filled), MockEncoding(filled)) @@ -23,9 +36,9 @@ def __init__(self, encoding): @pytest.mark.parametrize('stdEncoding', [ - pytest.raises(AttributeError), - pytest.raises(IOError), - lambda: 'ascii' + MockEncoding(AttributeError), + MockEncoding(IOError), + MockEncoding('ascii') ]) def test_detect_console_encoding_fallback_to_locale(monkeypatch, stdEncoding): # GH 21552 @@ -36,10 +49,12 @@ def test_detect_console_encoding_fallback_to_locale(monkeypatch, stdEncoding): @pytest.mark.parametrize('std,locale', [ - ['ascii', 'ascii'], - ['ascii', pytest.raises(Exception)], - [pytest.raises(Exception), 'ascii'], - [pytest.raises(Exception), pytest.raises(Exception)] + [MockEncoding('ascii'), 'ascii'], + [MockEncoding('ascii'), object()], + [MockEncoding(AttributeError), 'ascii'], + [MockEncoding(AttributeError), object()], + [MockEncoding(IOError), 'ascii'], + [MockEncoding(IOError), object()] ]) def test_detect_console_encoding_fallback_to_default(monkeypatch, std, locale): # When both the stdout/stdin encoding and locale preferred encoding checks From 7cedfdc565cee2178a490c980b5e9d870a784acc Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Wed, 25 Jul 2018 09:36:41 -0500 Subject: [PATCH 10/13] Add TODO(py27) to test helper. * Also remove the use of object() when replacing locale getpreferredencoding. This was originally used to trigger an attribute error which was caught by the blanket 'except Exception'. Instead, replace this function definition with a lambda that just raises an exception. --- pandas/tests/io/formats/test_console.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index 15b2e32cc7740..5c1bbac227daf 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -3,6 +3,7 @@ from pandas.io.formats.console import detect_console_encoding +# TODO(py27): replace with mock class MockEncoding(object): """ Used to add a side effect when accessing the 'encoding' property. If the @@ -15,10 +16,13 @@ def __init__(self, encoding): @property def encoding(self): - if isinstance(self.val, str): - return self.val - else: - raise self.val + return raiseOrReturn(self.val) + +def raiseOrReturn(val): + if isinstance(val, str): + return val + else: + raise val @pytest.mark.parametrize('empty,filled', [ @@ -49,12 +53,12 @@ def test_detect_console_encoding_fallback_to_locale(monkeypatch, stdEncoding): @pytest.mark.parametrize('std,locale', [ - [MockEncoding('ascii'), 'ascii'], - [MockEncoding('ascii'), object()], - [MockEncoding(AttributeError), 'ascii'], - [MockEncoding(AttributeError), object()], - [MockEncoding(IOError), 'ascii'], - [MockEncoding(IOError), object()] + [MockEncoding('ascii'), lambda: 'ascii'], + [MockEncoding('ascii'), lambda: raiseOrReturn(Exception)], + [MockEncoding(AttributeError), lambda: 'ascii'], + [MockEncoding(AttributeError), lambda: raiseOrReturn(Exception)], + [MockEncoding(IOError), lambda: 'ascii'], + [MockEncoding(IOError), lambda: raiseOrReturn(Exception)] ]) def test_detect_console_encoding_fallback_to_default(monkeypatch, std, locale): # When both the stdout/stdin encoding and locale preferred encoding checks From 35c0b3b89c3edc482f84f7b3fdc6a86ad41b7351 Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Wed, 25 Jul 2018 13:12:28 -0500 Subject: [PATCH 11/13] Fix pylint error. --- pandas/tests/io/formats/test_console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index 5c1bbac227daf..dbfe83c8d3a3f 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -3,9 +3,9 @@ from pandas.io.formats.console import detect_console_encoding -# TODO(py27): replace with mock class MockEncoding(object): """ + TODO(py27): replace with mock Used to add a side effect when accessing the 'encoding' property. If the side effect is a str in nature, the value will be returned. Otherwise, the side effect should be an exception that will be raised. From 49ee7b5b79f429694fd57a1ea0f50a484732beeb Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Thu, 26 Jul 2018 11:21:11 -0500 Subject: [PATCH 12/13] Fix silly linting errors. Use pep8. * It might be apparent what I styleguide I use!! --- pandas/tests/io/formats/test_console.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index dbfe83c8d3a3f..a6511ac625e5c 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -3,9 +3,8 @@ from pandas.io.formats.console import detect_console_encoding -class MockEncoding(object): +class MockEncoding(object): # TODO(py27): replace with mock """ - TODO(py27): replace with mock Used to add a side effect when accessing the 'encoding' property. If the side effect is a str in nature, the value will be returned. Otherwise, the side effect should be an exception that will be raised. @@ -16,9 +15,10 @@ def __init__(self, encoding): @property def encoding(self): - return raiseOrReturn(self.val) + return raise_or_return(self.val) -def raiseOrReturn(val): + +def raise_or_return(val): if isinstance(val, str): return val else: @@ -39,26 +39,26 @@ def test_detect_console_encoding_from_stdout_stdin(monkeypatch, empty, filled): assert detect_console_encoding() == filled -@pytest.mark.parametrize('stdEncoding', [ +@pytest.mark.parametrize('encoding', [ MockEncoding(AttributeError), MockEncoding(IOError), MockEncoding('ascii') ]) -def test_detect_console_encoding_fallback_to_locale(monkeypatch, stdEncoding): +def test_detect_console_encoding_fallback_to_locale(monkeypatch, encoding): # GH 21552 with monkeypatch.context() as context: context.setattr('locale.getpreferredencoding', lambda: 'foo') - context.setattr('sys.stdout', stdEncoding) + context.setattr('sys.stdout', encoding) assert detect_console_encoding() == 'foo' @pytest.mark.parametrize('std,locale', [ [MockEncoding('ascii'), lambda: 'ascii'], - [MockEncoding('ascii'), lambda: raiseOrReturn(Exception)], + [MockEncoding('ascii'), lambda: raise_or_return(Exception)], [MockEncoding(AttributeError), lambda: 'ascii'], - [MockEncoding(AttributeError), lambda: raiseOrReturn(Exception)], + [MockEncoding(AttributeError), lambda: raise_or_return(Exception)], [MockEncoding(IOError), lambda: 'ascii'], - [MockEncoding(IOError), lambda: raiseOrReturn(Exception)] + [MockEncoding(IOError), lambda: raise_or_return(Exception)] ]) def test_detect_console_encoding_fallback_to_default(monkeypatch, std, locale): # When both the stdout/stdin encoding and locale preferred encoding checks From 40b3588e9f56b78e665a506183ddc87cf9621dac Mon Sep 17 00:00:00 2001 From: Jay Offerdahl Date: Fri, 3 Aug 2018 20:52:17 -0500 Subject: [PATCH 13/13] Move raise_or_return to MockEncoding; clean up parameters. --- pandas/tests/io/formats/test_console.py | 41 +++++++++++++------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index a6511ac625e5c..055763bf62d6e 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -15,14 +15,14 @@ def __init__(self, encoding): @property def encoding(self): - return raise_or_return(self.val) + return self.raise_or_return(self.val) - -def raise_or_return(val): - if isinstance(val, str): - return val - else: - raise val + @staticmethod + def raise_or_return(val): + if isinstance(val, str): + return val + else: + raise val @pytest.mark.parametrize('empty,filled', [ @@ -40,32 +40,35 @@ def test_detect_console_encoding_from_stdout_stdin(monkeypatch, empty, filled): @pytest.mark.parametrize('encoding', [ - MockEncoding(AttributeError), - MockEncoding(IOError), - MockEncoding('ascii') + AttributeError, + IOError, + 'ascii' ]) def test_detect_console_encoding_fallback_to_locale(monkeypatch, encoding): # GH 21552 with monkeypatch.context() as context: context.setattr('locale.getpreferredencoding', lambda: 'foo') - context.setattr('sys.stdout', encoding) + context.setattr('sys.stdout', MockEncoding(encoding)) assert detect_console_encoding() == 'foo' @pytest.mark.parametrize('std,locale', [ - [MockEncoding('ascii'), lambda: 'ascii'], - [MockEncoding('ascii'), lambda: raise_or_return(Exception)], - [MockEncoding(AttributeError), lambda: 'ascii'], - [MockEncoding(AttributeError), lambda: raise_or_return(Exception)], - [MockEncoding(IOError), lambda: 'ascii'], - [MockEncoding(IOError), lambda: raise_or_return(Exception)] + ['ascii', 'ascii'], + ['ascii', Exception], + [AttributeError, 'ascii'], + [AttributeError, Exception], + [IOError, 'ascii'], + [IOError, Exception] ]) def test_detect_console_encoding_fallback_to_default(monkeypatch, std, locale): # When both the stdout/stdin encoding and locale preferred encoding checks # fail (or return 'ascii', we should default to the sys default encoding. # GH 21552 with monkeypatch.context() as context: - context.setattr('locale.getpreferredencoding', locale) - context.setattr('sys.stdout', std) + context.setattr( + 'locale.getpreferredencoding', + lambda: MockEncoding.raise_or_return(locale) + ) + context.setattr('sys.stdout', MockEncoding(std)) context.setattr('sys.getdefaultencoding', lambda: 'sysDefaultEncoding') assert detect_console_encoding() == 'sysDefaultEncoding'