Skip to content

Commit

Permalink
BUG: Allow IOErrors when attempting to retrieve default client encodi…
Browse files Browse the repository at this point in the history
  • Loading branch information
JayOfferdahl authored and aeltanawy committed Sep 20, 2018
1 parent 0ba7b16 commit 73ff71e
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 1 deletion.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.24.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ I/O
- :func:`read_sas()` will parse numbers in sas7bdat-files that have width less than 8 bytes correctly. (:issue:`21616`)
- :func:`read_sas()` will correctly parse sas7bdat files with many columns (:issue:`22628`)
- :func:`read_sas()` will correctly parse sas7bdat files with data page types having also bit 7 set (so page type is 128 + 256 = 384) (:issue:`16615`)
- 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
^^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion pandas/io/formats/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
74 changes: 74 additions & 0 deletions pandas/tests/io/formats/test_console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import pytest

from pandas.io.formats.console import detect_console_encoding


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.
"""
def __init__(self, encoding):
super(MockEncoding, self).__init__()
self.val = encoding

@property
def encoding(self):
return self.raise_or_return(self.val)

@staticmethod
def raise_or_return(val):
if isinstance(val, str):
return val
else:
raise val


@pytest.mark.parametrize('empty,filled', [
['stdin', 'stdout'],
['stdout', 'stdin']
])
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
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('encoding', [
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', MockEncoding(encoding))
assert detect_console_encoding() == 'foo'


@pytest.mark.parametrize('std,locale', [
['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',
lambda: MockEncoding.raise_or_return(locale)
)
context.setattr('sys.stdout', MockEncoding(std))
context.setattr('sys.getdefaultencoding', lambda: 'sysDefaultEncoding')
assert detect_console_encoding() == 'sysDefaultEncoding'

0 comments on commit 73ff71e

Please sign in to comment.