Skip to content

Commit

Permalink
Merge pull request #770 from minrk/jsonclean
Browse files Browse the repository at this point in the history
Ensures all replies from ipkernel are clean for json (not just oinfo), and guesses stdin.encoding before using sys.getdefaultencoding in json_clean.
  • Loading branch information
fperez committed Sep 12, 2011
2 parents ca3e756 + bc4e206 commit ede7936
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 10 deletions.
7 changes: 4 additions & 3 deletions IPython/config/loader.py
Expand Up @@ -24,7 +24,7 @@


from IPython.external import argparse from IPython.external import argparse
from IPython.utils.path import filefind, get_ipython_dir from IPython.utils.path import filefind, get_ipython_dir
from IPython.utils import py3compat, warn from IPython.utils import py3compat, text, warn


#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# Exceptions # Exceptions
Expand Down Expand Up @@ -425,7 +425,7 @@ def _decode_argv(self, argv, enc=None):
"""decode argv if bytes, using stin.encoding, falling back on default enc""" """decode argv if bytes, using stin.encoding, falling back on default enc"""
uargv = [] uargv = []
if enc is None: if enc is None:
enc = sys.stdin.encoding or sys.getdefaultencoding() enc = text.getdefaultencoding()
for arg in argv: for arg in argv:
if not isinstance(arg, unicode): if not isinstance(arg, unicode):
# only decode if not already decoded # only decode if not already decoded
Expand Down Expand Up @@ -586,7 +586,8 @@ def _add_arguments(self, aliases=None, flags=None):
def _parse_args(self, args): def _parse_args(self, args):
"""self.parser->self.parsed_data""" """self.parser->self.parsed_data"""
# decode sys.argv to support unicode command-line options # decode sys.argv to support unicode command-line options
uargs = [py3compat.cast_unicode(a) for a in args] enc = text.getdefaultencoding()
uargs = [py3compat.cast_unicode(a, enc) for a in args]
self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs) self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)


def _convert_to_config(self): def _convert_to_config(self):
Expand Down
3 changes: 2 additions & 1 deletion IPython/utils/_process_win32.py
Expand Up @@ -23,6 +23,7 @@


# our own imports # our own imports
from ._process_common import read_no_interrupt, process_handler from ._process_common import read_no_interrupt, process_handler
from . import text


#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# Function definitions # Function definitions
Expand Down Expand Up @@ -88,7 +89,7 @@ def _find_cmd(cmd):


def _system_body(p): def _system_body(p):
"""Callback for _system.""" """Callback for _system."""
enc = sys.stdin.encoding or sys.getdefaultencoding() enc = text.getdefaultencoding()
for line in read_no_interrupt(p.stdout).splitlines(): for line in read_no_interrupt(p.stdout).splitlines():
line = line.decode(enc, 'replace') line = line.decode(enc, 'replace')
print(line, file=sys.stdout) print(line, file=sys.stdout)
Expand Down
3 changes: 2 additions & 1 deletion IPython/utils/jsonutil.py
Expand Up @@ -17,6 +17,7 @@
from datetime import datetime from datetime import datetime


from IPython.utils import py3compat from IPython.utils import py3compat
from IPython.utils import text
next_attr_name = '__next__' if py3compat.PY3 else 'next' next_attr_name = '__next__' if py3compat.PY3 else 'next'


#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
Expand Down Expand Up @@ -134,7 +135,7 @@ def json_clean(obj):
return obj return obj


if isinstance(obj, bytes): if isinstance(obj, bytes):
return obj.decode(sys.getdefaultencoding(), 'replace') return obj.decode(text.getdefaultencoding(), 'replace')


if isinstance(obj, container_to_list) or ( if isinstance(obj, container_to_list) or (
hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)): hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)):
Expand Down
24 changes: 24 additions & 0 deletions IPython/utils/text.py
Expand Up @@ -16,9 +16,11 @@


import __main__ import __main__


import locale
import os import os
import re import re
import shutil import shutil
import sys
import textwrap import textwrap
from string import Formatter from string import Formatter


Expand All @@ -31,6 +33,28 @@
# Code # Code
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------


# Less conservative replacement for sys.getdefaultencoding, that will try
# to match the environment.
# Defined here as central function, so if we find better choices, we
# won't need to make changes all over IPython.
def getdefaultencoding():
"""Return IPython's guess for the default encoding for bytes as text.
Asks for stdin.encoding first, to match the calling Terminal, but that
is often None for subprocesses. Fall back on locale.getpreferredencoding()
which should be a sensible platform default (that respects LANG environment),
and finally to sys.getdefaultencoding() which is the most conservative option,
and usually ASCII.
"""
enc = sys.stdin.encoding
if not enc:
try:
# There are reports of getpreferredencoding raising errors
# in some cases, which may well be fixed, but let's be conservative here.
enc = locale.getpreferredencoding(False)
except Exception:
pass
return enc or sys.getdefaultencoding()


def unquote_ends(istr): def unquote_ends(istr):
"""Remove a single pair of quotes from the endpoints of a string.""" """Remove a single pair of quotes from the endpoints of a string."""
Expand Down
4 changes: 2 additions & 2 deletions IPython/zmq/iostream.py
Expand Up @@ -4,7 +4,7 @@


from session import extract_header, Message from session import extract_header, Message


from IPython.utils import io from IPython.utils import io, text


#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# Globals # Globals
Expand Down Expand Up @@ -69,7 +69,7 @@ def write(self, string):
else: else:
# Make sure that we're handling unicode # Make sure that we're handling unicode
if not isinstance(string, unicode): if not isinstance(string, unicode):
enc = sys.stdin.encoding or sys.getdefaultencoding() enc = text.getdefaultencoding()
string = string.decode(enc, 'replace') string = string.decode(enc, 'replace')


self._buffer.write(string) self._buffer.write(string)
Expand Down
5 changes: 4 additions & 1 deletion IPython/zmq/ipkernel.py
Expand Up @@ -303,6 +303,7 @@ def execute_request(self, ident, parent):
time.sleep(self._execute_sleep) time.sleep(self._execute_sleep)


# Send the reply. # Send the reply.
reply_content = json_clean(reply_content)
reply_msg = self.session.send(self.shell_socket, u'execute_reply', reply_msg = self.session.send(self.shell_socket, u'execute_reply',
reply_content, parent, ident=ident) reply_content, parent, ident=ident)
self.log.debug(str(reply_msg)) self.log.debug(str(reply_msg))
Expand All @@ -321,6 +322,7 @@ def complete_request(self, ident, parent):
matches = {'matches' : matches, matches = {'matches' : matches,
'matched_text' : txt, 'matched_text' : txt,
'status' : 'ok'} 'status' : 'ok'}
matches = json_clean(matches)
completion_msg = self.session.send(self.shell_socket, 'complete_reply', completion_msg = self.session.send(self.shell_socket, 'complete_reply',
matches, parent, ident) matches, parent, ident)
self.log.debug(str(completion_msg)) self.log.debug(str(completion_msg))
Expand Down Expand Up @@ -358,6 +360,7 @@ def history_request(self, ident, parent):
else: else:
hist = [] hist = []
content = {'history' : list(hist)} content = {'history' : list(hist)}
content = json_clean(content)
msg = self.session.send(self.shell_socket, 'history_reply', msg = self.session.send(self.shell_socket, 'history_reply',
content, parent, ident) content, parent, ident)
self.log.debug(str(msg)) self.log.debug(str(msg))
Expand Down Expand Up @@ -409,7 +412,7 @@ def _raw_input(self, prompt, ident, parent):
sys.stdout.flush() sys.stdout.flush()


# Send the input request. # Send the input request.
content = dict(prompt=prompt) content = json_clean(dict(prompt=prompt))
msg = self.session.send(self.stdin_socket, u'input_request', content, parent) msg = self.session.send(self.stdin_socket, u'input_request', content, parent)


# Await a response. # Await a response.
Expand Down
5 changes: 3 additions & 2 deletions IPython/zmq/zmqshell.py
Expand Up @@ -30,6 +30,7 @@
from IPython.core.magic import MacroToEdit from IPython.core.magic import MacroToEdit
from IPython.core.payloadpage import install_payload_page from IPython.core.payloadpage import install_payload_page
from IPython.utils import io from IPython.utils import io
from IPython.utils.jsonutil import json_clean
from IPython.utils.path import get_py_filename from IPython.utils.path import get_py_filename
from IPython.utils.traitlets import Instance, Type, Dict, CBool from IPython.utils.traitlets import Instance, Type, Dict, CBool
from IPython.utils.warn import warn from IPython.utils.warn import warn
Expand Down Expand Up @@ -69,7 +70,7 @@ def publish(self, source, data, metadata=None):
content['data'] = data content['data'] = data
content['metadata'] = metadata content['metadata'] = metadata
self.session.send( self.session.send(
self.pub_socket, u'display_data', content, self.pub_socket, u'display_data', json_clean(content),
parent=self.parent_header parent=self.parent_header
) )


Expand Down Expand Up @@ -144,7 +145,7 @@ def _showtraceback(self, etype, evalue, stb):
dh = self.displayhook dh = self.displayhook
# Send exception info over pub socket for other clients than the caller # Send exception info over pub socket for other clients than the caller
# to pick up # to pick up
exc_msg = dh.session.send(dh.pub_socket, u'pyerr', exc_content, dh.parent_header) exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header)


# FIXME - Hack: store exception info in shell object. Right now, the # FIXME - Hack: store exception info in shell object. Right now, the
# caller is reading this info after the fact, we need to fix this logic # caller is reading this info after the fact, we need to fix this logic
Expand Down

0 comments on commit ede7936

Please sign in to comment.