##// END OF EJS Templates
Handle unicode properly in IPython.zmq.iostream
Thomas Kluyver -
Show More
@@ -1,97 +1,93 b''
1 import logging
1 import logging
2 import sys
2 import sys
3 import time
3 import time
4 from cStringIO import StringIO
4 from io import StringIO
5
5
6 from session import extract_header, Message
6 from session import extract_header, Message
7
7
8 from IPython.utils import io
8 from IPython.utils import io
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Globals
11 # Globals
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 # Module-level logger
14 # Module-level logger
15 logger = logging.getLogger(__name__)
15 logger = logging.getLogger(__name__)
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Stream classes
18 # Stream classes
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 class OutStream(object):
21 class OutStream(object):
22 """A file like object that publishes the stream to a 0MQ PUB socket."""
22 """A file like object that publishes the stream to a 0MQ PUB socket."""
23
23
24 # The time interval between automatic flushes, in seconds.
24 # The time interval between automatic flushes, in seconds.
25 flush_interval = 0.05
25 flush_interval = 0.05
26 topic=None
26 topic=None
27
27
28 def __init__(self, session, pub_socket, name):
28 def __init__(self, session, pub_socket, name):
29 self.session = session
29 self.session = session
30 self.pub_socket = pub_socket
30 self.pub_socket = pub_socket
31 self.name = name
31 self.name = name
32 self.parent_header = {}
32 self.parent_header = {}
33 self._new_buffer()
33 self._new_buffer()
34
34
35 def set_parent(self, parent):
35 def set_parent(self, parent):
36 self.parent_header = extract_header(parent)
36 self.parent_header = extract_header(parent)
37
37
38 def close(self):
38 def close(self):
39 self.pub_socket = None
39 self.pub_socket = None
40
40
41 def flush(self):
41 def flush(self):
42 #io.rprint('>>>flushing output buffer: %s<<<' % self.name) # dbg
42 #io.rprint('>>>flushing output buffer: %s<<<' % self.name) # dbg
43 if self.pub_socket is None:
43 if self.pub_socket is None:
44 raise ValueError(u'I/O operation on closed file')
44 raise ValueError(u'I/O operation on closed file')
45 else:
45 else:
46 data = self._buffer.getvalue()
46 data = self._buffer.getvalue()
47 if data:
47 if data:
48 # Make sure that we're handling unicode
49 if not isinstance(data, unicode):
50 enc = sys.stdin.encoding or sys.getdefaultencoding()
51 data = data.decode(enc, 'replace')
52 content = {u'name':self.name, u'data':data}
48 content = {u'name':self.name, u'data':data}
53 msg = self.session.send(self.pub_socket, u'stream', content=content,
49 msg = self.session.send(self.pub_socket, u'stream', content=content,
54 parent=self.parent_header, ident=self.topic)
50 parent=self.parent_header, ident=self.topic)
55 logger.debug(msg)
51 logger.debug(msg)
56
52
57 self._buffer.close()
53 self._buffer.close()
58 self._new_buffer()
54 self._new_buffer()
59
55
60 def isatty(self):
56 def isatty(self):
61 return False
57 return False
62
58
63 def next(self):
59 def next(self):
64 raise IOError('Read not supported on a write only stream.')
60 raise IOError('Read not supported on a write only stream.')
65
61
66 def read(self, size=-1):
62 def read(self, size=-1):
67 raise IOError('Read not supported on a write only stream.')
63 raise IOError('Read not supported on a write only stream.')
68
64
69 def readline(self, size=-1):
65 def readline(self, size=-1):
70 raise IOError('Read not supported on a write only stream.')
66 raise IOError('Read not supported on a write only stream.')
71
67
72 def write(self, string):
68 def write(self, string):
73 if self.pub_socket is None:
69 if self.pub_socket is None:
74 raise ValueError('I/O operation on closed file')
70 raise ValueError('I/O operation on closed file')
75 else:
71 else:
76 # We can only send raw bytes, not unicode objects, so we encode
72 # Make sure that we're handling unicode
77 # into utf-8 for all frontends if we get unicode inputs.
73 if not isinstance(string, unicode):
78 if type(string) == unicode:
74 enc = sys.stdin.encoding or sys.getdefaultencoding()
79 string = string.encode('utf-8')
75 string = string.decode(enc, 'replace')
80
76
81 self._buffer.write(string)
77 self._buffer.write(string)
82 current_time = time.time()
78 current_time = time.time()
83 if self._start <= 0:
79 if self._start <= 0:
84 self._start = current_time
80 self._start = current_time
85 elif current_time - self._start > self.flush_interval:
81 elif current_time - self._start > self.flush_interval:
86 self.flush()
82 self.flush()
87
83
88 def writelines(self, sequence):
84 def writelines(self, sequence):
89 if self.pub_socket is None:
85 if self.pub_socket is None:
90 raise ValueError('I/O operation on closed file')
86 raise ValueError('I/O operation on closed file')
91 else:
87 else:
92 for string in sequence:
88 for string in sequence:
93 self.write(string)
89 self.write(string)
94
90
95 def _new_buffer(self):
91 def _new_buffer(self):
96 self._buffer = StringIO()
92 self._buffer = StringIO()
97 self._start = -1
93 self._start = -1
General Comments 0
You need to be logged in to leave comments. Login now