Skip to content

Commit dde1209

Browse files
author
Shane Hathaway
committed
Fixed logfile output to special files like /dev/stdout on Python 3. (Python 3.5.2 disallows open('/dev/stdout', 'a') but allows open('/dev/stdout', 'w').)
1 parent f4c55ba commit dde1209

File tree

2 files changed

+38
-3
lines changed

2 files changed

+38
-3
lines changed

supervisor/loggers.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import os
1111
import errno
12+
import stat
1213
import sys
1314
import time
1415
import traceback
@@ -138,11 +139,29 @@ class FileHandler(Handler):
138139
"""File handler which supports reopening of logs.
139140
"""
140141

141-
def __init__(self, filename, mode="a"):
142+
def __init__(self, filename, mode=None):
142143
Handler.__init__(self)
143-
self.stream = open(filename, mode)
144+
open_mode = mode or 'a'
145+
146+
try:
147+
self.stream = open(filename, open_mode)
148+
except OSError as e:
149+
retried = False
150+
if not mode:
151+
if e.errno == errno.ESPIPE:
152+
st_mode = os.stat(filename).st_mode
153+
if stat.S_ISCHR(st_mode) or stat.S_ISFIFO(st_mode):
154+
# Python 3 can't open special files like
155+
# /dev/stdout in 'a' mode due to an implicit seek call
156+
# that fails with ESPIPE. Retry in 'w' mode.
157+
self.stream = open(filename, 'w')
158+
open_mode = 'w'
159+
retried = True
160+
if not retried:
161+
raise
162+
144163
self.baseFilename = filename
145-
self.mode = mode
164+
self.mode = open_mode
146165

147166
def reopen(self):
148167
self.close()

supervisor/tests/test_loggers.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,22 @@ def test_emit_error(self):
220220
self.assertTrue(dummy_stderr.written.endswith('OSError\n'),
221221
dummy_stderr.written)
222222

223+
if os.path.exists('/dev/stdout'):
224+
StdoutTestsBase = FileHandlerTests
225+
else:
226+
# Skip the stdout tests on platforms that don't have /dev/stdout.
227+
StdoutTestsBase = object
228+
229+
class StdoutTests(StdoutTestsBase):
230+
def test_ctor_with_dev_stdout(self):
231+
handler = self._makeOne('/dev/stdout')
232+
# Modes 'w' and 'a' have the same semantics when applied to
233+
# character device files and fifos.
234+
self.assertIn(handler.mode, ['w', 'a'])
235+
self.assertEqual(handler.baseFilename, '/dev/stdout')
236+
self.assertEqual(handler.stream.name, '/dev/stdout')
237+
handler.close()
238+
223239
class RotatingFileHandlerTests(FileHandlerTests):
224240

225241
def _getTargetClass(self):

0 commit comments

Comments
 (0)