Skip to content

Commit 189ccfe

Browse files
committed
DVPL-7646: fix generic ofile binary write issue
1 parent 3ce5f56 commit 189ccfe

File tree

1 file changed

+52
-61
lines changed

1 file changed

+52
-61
lines changed

splunklib/searchcommands/internals.py

Lines changed: 52 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,37 @@
4040

4141
csv.field_size_limit(10485760) # The default value is 128KB; upping to 10MB. See SPL-12117 for background on this issue
4242

43-
# SPL-175233 -- python3 stdout is already binary
44-
if sys.platform == 'win32' and sys.version_info <= (3, 0):
45-
# Work around the fact that on Windows '\n' is mapped to '\r\n'. The typical solution is to simply open files in
46-
# binary mode, but stdout is already open, thus this hack. 'CPython' and 'PyPy' work differently. We assume that
47-
# all other Python implementations are compatible with 'CPython'. This might or might not be a valid assumption.
48-
from platform import python_implementation
49-
implementation = python_implementation()
50-
fileno = sys.stdout.fileno()
51-
if implementation == 'PyPy':
52-
sys.stdout = os.fdopen(fileno, 'wb', 0)
53-
else:
54-
from msvcrt import setmode
55-
setmode(fileno, os.O_BINARY)
43+
44+
def set_binary_mode(fh):
45+
""" Helper method to set up binary mode for file handles.
46+
Emphasis being sys.stdin, sys.stdout, sys.stderr.
47+
For python3, we want to return .buffer
48+
For python2+windows we want to set os.O_BINARY
49+
"""
50+
typefile = TextIOWrapper if sys.version_info >= (3, 0) else file
51+
# check for file handle
52+
if not isinstance(fh, typefile):
53+
return fh
54+
55+
# check for python3 and buffer
56+
if sys.version_info >= (3, 0) and hasattr(fh, 'buffer'):
57+
return fh.buffer
58+
# check for python3
59+
elif sys.version_info >= (3, 0):
60+
pass
61+
# check for windows python2. SPL-175233 -- python3 stdout is already binary
62+
elif sys.platform == 'win32':
63+
# Work around the fact that on Windows '\n' is mapped to '\r\n'. The typical solution is to simply open files in
64+
# binary mode, but stdout is already open, thus this hack. 'CPython' and 'PyPy' work differently. We assume that
65+
# all other Python implementations are compatible with 'CPython'. This might or might not be a valid assumption.
66+
from platform import python_implementation
67+
implementation = python_implementation()
68+
if implementation == 'PyPy':
69+
return os.fdopen(fh.fileno(), 'wb', 0)
70+
else:
71+
import msvcrt
72+
msvcrt.setmode(fh.fileno(), os.O_BINARY)
73+
return fh
5674

5775

5876
class CommandLineParser(object):
@@ -350,6 +368,7 @@ class InputHeader(dict):
350368
""" Represents a Splunk input header as a collection of name/value pairs.
351369
352370
"""
371+
353372
def __str__(self):
354373
return '\n'.join([name + ':' + value for name, value in six.iteritems(self)])
355374

@@ -377,7 +396,8 @@ def read(self, ifile):
377396
# continuation of the current item
378397
value += urllib.parse.unquote(line)
379398

380-
if name is not None: self[name] = value[:-1] if value[-1] == '\n' else value
399+
if name is not None:
400+
self[name] = value[:-1] if value[-1] == '\n' else value
381401

382402

383403
Message = namedtuple('Message', ('type', 'text'))
@@ -474,7 +494,7 @@ class RecordWriter(object):
474494
def __init__(self, ofile, maxresultrows=None):
475495
self._maxresultrows = 50000 if maxresultrows is None else maxresultrows
476496

477-
self._ofile = ofile
497+
self._ofile = set_binary_mode(ofile)
478498
self._fieldnames = None
479499
self._buffer = StringIO()
480500

@@ -502,7 +522,13 @@ def ofile(self):
502522

503523
@ofile.setter
504524
def ofile(self, value):
505-
self._ofile = value
525+
self._ofile = set_binary_mode(value)
526+
527+
def write(self, data):
528+
bytes_type = bytes if sys.version_info >= (3, 0) else str
529+
if not isinstance(data, bytes_type):
530+
data = data.encode('utf-8')
531+
self.ofile.write(data)
506532

507533
def flush(self, finished=None, partial=None):
508534
assert finished is None or isinstance(finished, bool)
@@ -662,19 +688,11 @@ class RecordWriterV1(RecordWriter):
662688

663689
def flush(self, finished=None, partial=None):
664690

665-
# SPL-175233
666-
def writeEOL():
667-
if sys.version_info >= (3, 0) and sys.platform == 'win32':
668-
write('\n')
669-
else:
670-
write('\r\n')
671-
672691
RecordWriter.flush(self, finished, partial) # validates arguments and the state of this instance
673692

674693
if self._record_count > 0 or (self._chunk_count == 0 and 'messages' in self._inspector):
675694

676695
messages = self._inspector.get('messages')
677-
write = self._ofile.write
678696

679697
if self._chunk_count == 0:
680698

@@ -686,12 +704,12 @@ def writeEOL():
686704
message_level = RecordWriterV1._message_level.get
687705

688706
for level, text in messages:
689-
write(message_level(level, level))
690-
write('=')
691-
write(text)
692-
writeEOL()
707+
self.write(message_level(level, level))
708+
self.write('=')
709+
self.write(text)
710+
self.write('\r\n')
693711

694-
writeEOL()
712+
self.write('\r\n')
695713

696714
elif messages is not None:
697715

@@ -709,7 +727,7 @@ def writeEOL():
709727
for level, text in messages:
710728
print(level, text, file=stderr)
711729

712-
write(self._buffer.getvalue())
730+
self.write(self._buffer.getvalue())
713731
self._clear()
714732
self._chunk_count += 1
715733
self._total_record_count += self._record_count
@@ -767,7 +785,7 @@ def write_metadata(self, configuration):
767785

768786
metadata = chain(six.iteritems(configuration), (('inspector', self._inspector if self._inspector else None),))
769787
self._write_chunk(metadata, '')
770-
self._ofile.write('\n')
788+
self.write('\n')
771789
self._clear()
772790

773791
def write_metric(self, name, value):
@@ -796,35 +814,8 @@ def _write_chunk(self, metadata, body):
796814
return
797815

798816
start_line = 'chunked 1.0,%s,%s\n' % (metadata_length, body_length)
799-
ofile_handle = set_binary_mode(self._ofile)
800-
write = ofile_handle.write
801-
write(start_line.encode('utf-8'))
802-
write(metadata)
803-
write(body)
817+
self.write(start_line)
818+
self.write(metadata)
819+
self.write(body)
804820
self._ofile.flush()
805821
self._flushed = False
806-
807-
def set_binary_mode(fh):
808-
""" Helper method to set up binary mode for file handles.
809-
Emphasis being sys.stdin, sys.stdout, sys.stderr.
810-
For python3, we want to return .buffer
811-
For python2+windows we want to set os.O_BINARY
812-
"""
813-
typefile = TextIOWrapper if sys.version_info >= (3, 0) else file
814-
# check for file handle
815-
if not isinstance(fh, typefile):
816-
return fh
817-
818-
# check for python3 and buffer
819-
if sys.version_info >= (3, 0) and hasattr(fh, 'buffer'):
820-
return fh.buffer
821-
# check for python3
822-
elif sys.version_info >= (3, 0):
823-
pass
824-
# check for windows
825-
elif sys.platform == 'win32':
826-
import msvcrt
827-
msvcrt.setmode(fh.fileno(), os.O_BINARY)
828-
829-
return fh
830-

0 commit comments

Comments
 (0)