Skip to content

Commit

Permalink
Python 3 changes for SearchCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
amysutedja committed May 14, 2020
1 parent 44e89d0 commit a8d4b6c
Show file tree
Hide file tree
Showing 11 changed files with 52 additions and 32 deletions.
21 changes: 10 additions & 11 deletions examples/async/async.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
# based systems like Eventlet.

#### Main Code

from __future__ import absolute_import
from __future__ import print_function
import sys, os, datetime
Expand Down Expand Up @@ -67,10 +66,10 @@ def main(argv):
# of `urllib2`. Otherwise, import the stdlib version of `urllib2`.
#
# The reason for the funky import syntax is that Python imports
# are scoped to functions, and we need to make it global.
# are scoped to functions, and we need to make it global.
# In a real application, you would only import one of these.
if is_async:
urllib2 = __import__('eventlet.green', globals(), locals(),
urllib2 = __import__('eventlet.green', globals(), locals(),
['urllib2'], -1).urllib2
else:
urllib2 = __import__("urllib2", globals(), locals(), [], -1)
Expand Down Expand Up @@ -143,12 +142,12 @@ def do_search(query):
# and we can also ignore the result.
for query in queries:
do_search(query)

# Record the current time at the end of the benchmark,
# and print the delta elapsed time.
newtime = datetime.datetime.now()
print("Elapsed Time: %s" % (newtime - oldtime))


##### Custom `urllib2`-based HTTP handler

Expand All @@ -158,21 +157,21 @@ def request(url, message, **kwargs):
body = message.get("body", "")

# Setup the default headers.
head = {
head = {
"Content-Length": str(len(body)),
"Host": host,
"User-Agent": "http.py/1.0",
"Accept": "*/*",
}

# Add in the passed in headers.
for key, value in message["headers"]:
for key, value in message["headers"]:
head[key] = value

# Note the HTTP method we're using, defaulting
# to `GET`.
method = message.get("method", "GET")

# Note that we do not support proxies in this example
# If running Python 2.7.9+, disable SSL certificate validation
if sys.version_info >= (2, 7, 9):
Expand All @@ -181,7 +180,7 @@ def request(url, message, **kwargs):
else:
opener = urllib2.build_opener()

# Unfortunately, we need to use the hack of
# Unfortunately, we need to use the hack of
# "overriding" `request.get_method` to specify
# a method other than `GET` or `POST`.
request = urllib2.Request(url, body, head)
Expand All @@ -194,10 +193,10 @@ def request(url, message, **kwargs):
except Exception as e:
response = e

# Normalize the response to something the SDK expects, and
# Normalize the response to something the SDK expects, and
# return it.
return {
'status': response.code,
'status': response.code,
'reason': response.msg,
'headers': response.info().dict,
'body': binding.ResponseReader(response)
Expand Down
2 changes: 1 addition & 1 deletion examples/searchcommands_app/package/bin/countmatches.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def stream(self, records):
for record in records:
count = 0
for fieldname in self.fieldnames:
matches = pattern.findall(six.text_type(record[fieldname].decode("utf-8")))
matches = pattern.findall(six.text_type(six.ensure_binary(record[fieldname]).decode("utf-8")))
count += len(matches)
record[self.fieldname] = count
yield record
Expand Down
4 changes: 2 additions & 2 deletions splunklib/searchcommands/reporting_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def fix_up(cls, command):
cls._requires_preop = False
return

f = vars(command)[b'map'] # Function backing the map method
f = vars(command)['map'] # Function backing the map method

# EXPLANATION OF PREVIOUS STATEMENT: There is no way to add custom attributes to methods. See [Why does
# setattr fail on a method](http://stackoverflow.com/questions/7891277/why-does-setattr-fail-on-a-bound-method) for a discussion of this issue.
Expand All @@ -266,7 +266,7 @@ def fix_up(cls, command):

# Create new StreamingCommand.ConfigurationSettings class

module = command.__module__ + b'.' + command.__name__ + b'.map'
module = command.__module__ + '.' + command.__name__ + '.map'
name = b'ConfigurationSettings'
bases = (StreamingCommand.ConfigurationSettings,)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# TODO: Flesh this out
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

total,__mv_total
2147943.07810599,
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

total,__mv_total
2147943.07811,
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

requires_preop,__mv_requires_preop,retainsevents,__mv_retainsevents,streaming,__mv_streaming,streaming_preop,__mv_streaming_preop
1,,0,,0,,"sum phase=""map"" record=""f"" total=""total"" value1",
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
chunked 1.0,20,0
{"type":"streaming"}
chunked 1.0,17,37
{"finished":true}total,__mv_total
2147943.07810599,
29 changes: 16 additions & 13 deletions tests/searchcommands/test_searchcommands_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,20 @@ def __init__(self, path):
splunk_cmd = path + '.splunk_cmd'

try:
with io.open(splunk_cmd, 'rb') as f:
with io.open(splunk_cmd, 'r') as f:
self._args = f.readline().encode().split(None, 5) # ['splunk', 'cmd', <filename>, <action>, <args>]
except IOError as error:
if error.errno != 2:
raise
self._args = ['splunk', 'cmd', 'python', None]

self._input_file = path + '.input.gz'

self._output_file = path + '.output'

if six.PY3 and os.path.isfile(self._output_file + '.py3'):
self._output_file = self._output_file + '.py3'

# Remove the "splunk cmd" portion
self._args = self._args[2:]

Expand Down Expand Up @@ -148,19 +152,15 @@ def __iter__(self):

@pytest.mark.smoke
class TestSearchCommandsApp(TestCase):

try:
app_root = os.path.join(project_root, 'examples', 'searchcommands_app', 'build', 'searchcommands_app')
except NameError:
# SKip if Python 2.6
pass
app_root = os.path.join(project_root, 'examples', 'searchcommands_app', 'build', 'searchcommands_app')

def setUp(self):
if not os.path.isdir(TestSearchCommandsApp.app_root):
build_command = os.path.join(project_root, 'examples', 'searchcommands_app', 'setup.py build')
self.skipTest("You must build the searchcommands_app by running " + build_command)
TestCase.setUp(self)

@pytest.mark.skipif(six.PY3, reason="Python 2 does not treat Unicode as words for regex, so Python 3 has broken fixtures")
def test_countmatches_as_unit(self):
expected, output, errors, exit_status = self._run_command('countmatches', action='getinfo', protocol=1)
self.assertEqual(0, exit_status, msg=six.text_type(errors))
Expand Down Expand Up @@ -270,12 +270,10 @@ def assertInfoEqual(self, output, expected):
self.assertDictEqual(expected, output)

def _compare_chunks(self, expected, output, time_sensitive=True):

expected = expected.strip()
output = output.strip()

if time_sensitive:
self.assertEqual(len(expected), len(output))
compare_csv_files = self._compare_csv_files_time_sensitive
else:
compare_csv_files = self._compare_csv_files_time_insensitive
Expand Down Expand Up @@ -326,11 +324,12 @@ def _compare_csv_files_time_insensitive(self, expected, output):

line_number += 1

self.assertRaises(StopIteration, output.next)
if six.PY2:
self.assertRaises(StopIteration, output.next)

return

def _compare_csv_files_time_sensitive(self, expected, output):

self.assertEqual(len(expected), len(output))

skip_first_row = expected[0:2] == '\r\n'
Expand All @@ -352,7 +351,9 @@ def _compare_csv_files_time_sensitive(self, expected, output):
line_number, expected_row, output_row))
line_number += 1

self.assertRaises(StopIteration, output.next)
if six.PY2:
self.assertRaises(StopIteration, output.next)

return

def _get_search_command_path(self, name):
Expand Down Expand Up @@ -419,17 +420,19 @@ def _run_command(self, name, action=None, phase=None, protocol=2):
ofile.write(b[:count])
break
ofile.write(b)

with io.open(uncompressed_file, 'rb') as ifile:
env = os.environ.copy()
env['PYTHONPATH'] = os.pathsep.join(sys.path)
process = Popen(recording.get_args(command), stdin=ifile, stderr=PIPE, stdout=PIPE, env=env)
output, errors = process.communicate()

with io.open(recording.output_file, 'rb') as ifile:
expected = ifile.read()
finally:
os.remove(uncompressed_file)

return expected, output, errors, process.returncode
return six.ensure_str(expected), six.ensure_str(output), six.ensure_str(errors), process.returncode

_Chunk = namedtuple('Chunk', 'metadata body')

Expand Down
11 changes: 7 additions & 4 deletions tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
import splunklib.client as client
from splunklib import six

DIR_PATH = os.path.dirname(os.path.realpath(__file__))
EXAMPLES_PATH = os.path.join(DIR_PATH, '..', 'examples')
BUILD_PATH = os.path.join(DIR_PATH, '..', 'build')

def check_multiline(testcase, first, second, message=None):
"""Assert that two multi-line strings are equal."""
Expand Down Expand Up @@ -62,7 +65,7 @@ def start(script, stdin=None, stdout=PIPE, stderr=None):
if isinstance(script, str):
script = script.split()
script = ["python"] + script
return Popen(script, stdin=stdin, stdout=stdout, stderr=stderr, cwd='../examples')
return Popen(script, stdin=stdin, stdout=stdout, stderr=stderr, cwd=EXAMPLES_PATH)


# Rudimentary sanity check for each of the examples
Expand All @@ -79,7 +82,7 @@ def setUp(self):
# Ignore result, it might already exist
run("index.py create sdk-tests")

@unittest.skipIf(six.PY3, "Async needs work to support Python 3")
@pytest.mark.skipif(six.PY3, reason="Async needs work to support Python 3")
def test_async(self):
result = run("async/async.py sync")
self.assertEqual(result, 0)
Expand All @@ -94,7 +97,7 @@ def test_async(self):
pass

def test_build_dir_exists(self):
self.assertTrue(os.path.exists("../build"), 'Run setup.py build, then setup.py dist')
self.assertTrue(os.path.exists(BUILD_PATH), 'Run setup.py build, then setup.py dist')

def test_binding1(self):
result = run("binding1.py")
Expand Down Expand Up @@ -264,7 +267,7 @@ def test_upload(self):
def test_analytics(self):
# We have to add the current path to the PYTHONPATH,
# otherwise the import doesn't work quite right
sys.path.append(os.getcwd())
sys.path.append(EXAMPLES_PATH)
import analytics

# Create a tracker
Expand Down
2 changes: 1 addition & 1 deletion tests/test_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def test_update(self):
entity.refresh()
self.assertEqual(entity.host, kwargs['host'])

@unittest.skip('flaky')
@pytest.mark.skip('flaky')
def test_delete(self):
inputs = self.service.inputs
remaining = len(self._test_entities)-1
Expand Down

0 comments on commit a8d4b6c

Please sign in to comment.