Skip to content

Commit a88c1f1

Browse files
committed
Initial cut for fix of issue Supervisor#1231.
1 parent 6bd04b6 commit a88c1f1

File tree

4 files changed

+91
-3
lines changed

4 files changed

+91
-3
lines changed

supervisor/http_client.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,23 @@ def done(self, url):
2929

3030
def feed(self, url, data):
3131
try:
32-
data = as_string(data)
32+
sdata = as_string(data)
3333
except UnicodeDecodeError:
34-
data = 'Undecodable: %r' % data
35-
sys.stdout.write(data)
34+
sdata = 'Undecodable: %r' % data
35+
# We've got Unicode data in sdata now, but writing to stdout sometimes
36+
# fails - see issue #1231.
37+
try:
38+
sys.stdout.write(sdata)
39+
except UnicodeEncodeError as e:
40+
if sys.version_info[0] < 3:
41+
# This might seem like The Wrong Thing To Do (writing bytes
42+
# rather than text to an output stream), but it seems to work
43+
# OK for Python 2.7.
44+
sys.stdout.write(data)
45+
else:
46+
s = ('Unable to write Unicode to stdout because it has '
47+
'encoding %s' % sys.stdout.encoding)
48+
raise ValueError(s)
3649
sys.stdout.flush()
3750

3851
def close(self, url):

supervisor/supervisorctl.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,14 @@ class DefaultControllerPlugin(ControllerPluginBase):
440440
name = 'default'
441441
listener = None # for unit tests
442442
def _tailf(self, path):
443+
def not_all_langs():
444+
enc = getattr(sys.stdout, 'encoding', '').lower()
445+
return None if enc.startswith('utf') else sys.stdout.encoding
446+
447+
problematic_enc = not_all_langs()
448+
if problematic_enc:
449+
self.ctl.output('Warning: sys.stdout.encoding is set to %s, so '
450+
'Unicode output may fail.' % problematic_enc)
443451
self.ctl.output('==> Press Ctrl-C to exit <==')
444452

445453
username = self.ctl.options.username
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# -*- coding: utf-8 -*-
2+
import logging
3+
import random
4+
import sys
5+
import time
6+
7+
def main():
8+
logging.basicConfig(level=logging.INFO, stream=sys.stdout,
9+
format='%(levelname)s [%(asctime)s] %(message)s',
10+
datefmt='%m-%d|%H:%M:%S')
11+
i = 1
12+
while True:
13+
delay = random.randint(400, 1200)
14+
time.sleep(delay / 1000.0)
15+
logging.info('%d - hash=57d94b…381088', i)
16+
i += 1
17+
18+
if __name__ == '__main__':
19+
main()

supervisor/tests/test_end_to_end.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,54 @@ def test_issue_1224(self):
136136
self.addCleanup(supervisord.kill, signal.SIGINT)
137137
supervisord.expect_exact('cat entered RUNNING state', timeout=60)
138138

139+
def test_issue_1231a(self):
140+
filename = pkg_resources.resource_filename(__name__, 'fixtures/issue-1231.conf')
141+
args = ['-m', 'supervisor.supervisord', '-c', filename]
142+
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
143+
self.addCleanup(supervisord.kill, signal.SIGINT)
144+
supervisord.expect_exact('success: hello entered RUNNING state')
145+
146+
args = ['-m', 'supervisor.supervisorctl', '-c', filename, 'tail', '-f', 'hello']
147+
supervisorctl = pexpect.spawn(sys.executable, args, encoding='utf-8')
148+
self.addCleanup(supervisorctl.kill, signal.SIGINT)
149+
150+
for i in range(1, 4):
151+
line = '%d - hash=57d94b…381088' % i
152+
supervisorctl.expect_exact(line, timeout=30)
153+
154+
155+
def test_issue_1231b(self):
156+
filename = pkg_resources.resource_filename(__name__, 'fixtures/issue-1231.conf')
157+
args = ['-m', 'supervisor.supervisord', '-c', filename]
158+
supervisord = pexpect.spawn(sys.executable, args, encoding='utf-8')
159+
self.addCleanup(supervisord.kill, signal.SIGINT)
160+
supervisord.expect_exact('success: hello entered RUNNING state')
161+
162+
args = ['-m', 'supervisor.supervisorctl', '-c', filename, 'tail', '-f', 'hello']
163+
env = os.environ.copy()
164+
env['LANG'] = 'oops'
165+
supervisorctl = pexpect.spawn(sys.executable, args, encoding='utf-8',
166+
env=env)
167+
self.addCleanup(supervisorctl.kill, signal.SIGINT)
168+
169+
# For Python 3 < 3.7, LANG=oops leads to warnings because of the
170+
# stdout encoding. For 3.7 (and presumably later), the encoding is
171+
# utf-8 when LANG=oops.
172+
if sys.version_info[:2] < (3, 7):
173+
supervisorctl.expect('Warning: sys.stdout.encoding is set to ',
174+
timeout=30)
175+
supervisorctl.expect('Unicode output may fail.', timeout=30)
176+
177+
for i in range(1, 4):
178+
line = '%d - hash=57d94b…381088' % i
179+
try:
180+
supervisorctl.expect_exact(line, timeout=30)
181+
except pexpect.exceptions.EOF:
182+
self.assertIn('Unable to write Unicode to stdout because it '
183+
'has encoding ',
184+
supervisorctl.before)
185+
break
186+
139187

140188
def test_suite():
141189
return unittest.findTestCases(sys.modules[__name__])

0 commit comments

Comments
 (0)