Skip to content

Commit f8ca38a

Browse files
author
Shakeel Mohamed
committed
Merge branch 'feature/dnoble-bug_fixes' into develop
2 parents 3cc7f76 + 6550577 commit f8ca38a

15 files changed

+1172
-1578
lines changed

examples/searchcommands_app/bin/simulate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class SimulateCommand(GeneratingCommand):
8686

8787
def generate(self):
8888
""" Yields one random record at a time for the duration of `duration` """
89-
self.logger.debug('SimulateCommand: %s' % self) # log command line
89+
self.logger.debug('SimulateCommand: %s', self) # log command line
9090
if not self.records:
9191
if self.seed is not None:
9292
random.seed(self.seed)

splunklib/searchcommands/decorators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ def __repr__(self):
222222
return str(self)
223223

224224
def __str__(self):
225-
value = self.validator.format(self.value) if self.validator is not None else str(self.value)
225+
value = str(self.value) if self.validator is None else self.validator.format(self.value)
226226
encoder = Option.Encoder(self)
227227
text = '='.join([self.name, encoder.encode(value)])
228228
return text

splunklib/searchcommands/logging.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
import os
2020
import sys
2121

22+
def get_app_directory(probing_path):
23+
return os.path.dirname(os.path.abspath(os.path.dirname(probing_path)))
2224

23-
def configure(name, path=None):
25+
def configure(name, probing_path=None, app_root=None):
2426
""" Configure logging and return a logger and the location of its logging
2527
configuration file.
2628
@@ -57,63 +59,66 @@ def configure(name, path=None):
5759
return.
5860
5961
You may short circuit the search for a logging configuration file by
60-
providing an alternative file location in `path`. Logging configuration
62+
providing an alternative file location in `probing_path`. Logging configuration
6163
files must be in `ConfigParser format`_.
6264
6365
#Arguments:
6466
6567
:param name: Logger name
6668
:type name: str
67-
:param path: Location of an alternative logging configuration file or `None`
68-
:type path: str or NoneType
69+
:param probing_path: Location of an alternative logging configuration file or `None`
70+
:type probing_path: str or NoneType
6971
:returns: A logger and the location of its logging configuration file
72+
:param app_root: The root of the application directory, used primarily by tests.
73+
:type app_root: str or NoneType
7074
7175
.. _ConfigParser format: http://goo.gl/K6edZ8
7276
7377
"""
74-
app_directory = os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))
7578

76-
if path is None:
77-
probing_path = [
79+
app_directory = get_app_directory(sys.argv[0]) if app_root is None else app_root
80+
81+
if probing_path is None:
82+
probing_paths = [
7883
'local/%s.logging.conf' % name,
7984
'default/%s.logging.conf' % name,
8085
'local/logging.conf',
8186
'default/logging.conf']
82-
for relative_path in probing_path:
87+
for relative_path in probing_paths:
8388
configuration_file = os.path.join(app_directory, relative_path)
8489
if os.path.exists(configuration_file):
85-
path = configuration_file
90+
probing_path = configuration_file
8691
break
87-
elif not os.path.isabs(path):
92+
elif not os.path.isabs(probing_path):
8893
found = False
8994
for conf in 'local', 'default':
90-
configuration_file = os.path.join(app_directory, conf, path)
95+
configuration_file = os.path.join(app_directory, conf, probing_path)
9196
if os.path.exists(configuration_file):
92-
path = configuration_file
97+
probing_path = configuration_file
9398
found = True
9499
break
95100
if not found:
96101
raise ValueError(
97102
'Logging configuration file "%s" not found in local or default '
98-
'directory' % path)
99-
elif not os.path.exists(path):
103+
'directory' % probing_path)
104+
elif not os.path.exists(probing_path):
100105
raise ValueError('Logging configuration file "%s" not found')
101106

102-
if path is not None:
107+
if probing_path is not None:
103108
working_directory = os.getcwd()
104109
os.chdir(app_directory)
105110
try:
106111
splunk_home = os.path.normpath(os.path.join(working_directory, os.environ['SPLUNK_HOME']))
107112
except KeyError:
108113
splunk_home = working_directory # reasonable in debug scenarios
109114
try:
110-
path = os.path.abspath(path)
111-
fileConfig(path, {'SPLUNK_HOME': splunk_home})
115+
probing_path = os.path.abspath(probing_path)
116+
fileConfig(probing_path, {'SPLUNK_HOME': splunk_home})
112117
finally:
113118
os.chdir(working_directory)
114119

115120
if len(root.handlers) == 0:
116121
root.addHandler(StreamHandler())
117122

118123
logger = getLogger(name)
119-
return logger, path
124+
return logger, probing_path

splunklib/searchcommands/search_command.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,15 @@ class SearchCommand(object):
4343
4444
"""
4545

46-
def __init__(self):
46+
def __init__(self, app_root=None):
47+
"""
48+
:param app_root: The root of the application directory, used primarily by tests.
49+
:type app_root: str or NoneType
50+
"""
4751

4852
# Variables that may be used, but not altered by derived classes
4953

50-
self.logger, self._logging_configuration = logging.configure(type(self).__name__)
54+
self.logger, self._logging_configuration = logging.configure(type(self).__name__, app_root=app_root)
5155
self.input_header = InputHeader()
5256
self.messages = MessagesHeader()
5357

@@ -61,6 +65,7 @@ def __init__(self):
6165

6266
# Variables backing option/property values
6367

68+
self._app_root = app_root
6469
self._default_logging_level = self.logger.level
6570
self._configuration = None
6671
self._fieldnames = None
@@ -95,7 +100,7 @@ def logging_configuration(self):
95100
@logging_configuration.setter
96101
def logging_configuration(self, value):
97102
self.logger, self._logging_configuration = logging.configure(
98-
type(self).__name__, value)
103+
type(self).__name__, value, app_root=self._app_root)
99104
return
100105

101106
@Option
@@ -274,9 +279,9 @@ def service(self):
274279

275280
#region Methods
276281

277-
def error_exit(self, error):
278-
self.logger.error('Abnormal exit: ' + error)
279-
self.write_error(error)
282+
def error_exit(self, error, message=None):
283+
self.logger.error('Abnormal exit: %s', error)
284+
self.write_error(error.message.capitalize() if message is None else message)
280285
exit(1)
281286

282287
def process(self, args=argv, input_file=stdin, output_file=stdout):

splunklib/searchcommands/search_command_internals.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,7 @@ def parse(self, argv, command):
275275

276276
# Parse options
277277

278-
for option in SearchCommandParser._options_re.finditer(
279-
command_args.group('options')):
278+
for option in SearchCommandParser._options_re.finditer(command_args.group('options')):
280279
name, value = option.group(1), option.group(2)
281280
if not name in command.options:
282281
raise ValueError('Unrecognized option: %s = %s' % (name, value))
@@ -288,9 +287,7 @@ def parse(self, argv, command):
288287
if len(missing) == 1:
289288
raise ValueError('A value for "%s" is required' % missing[0])
290289
else:
291-
raise ValueError(
292-
'Values for these options are required: %s' %
293-
', '.join(missing))
290+
raise ValueError('Values for these options are required: %s' % ', '.join(missing))
294291

295292
# Parse field names
296293

@@ -365,9 +362,9 @@ def replace(match):
365362

366363
_options_re = re.compile(r"""
367364
# Captures a set of name/value pairs when used with re.finditer
368-
([_a-zA-Z][_a-zA-Z0-9]+) # name
369-
\s*=\s* # =
370-
([^\s"]+|"(?:[^"]+|""|\\")*") # value
365+
([_a-zA-Z][_a-zA-Z0-9]+) # name
366+
\s*=\s* # =
367+
([^\s"]+|"(?:[^\\"]+|\\.|"")*") # value
371368
""", re.VERBOSE)
372369

373370
#endregion

splunklib/searchcommands/splunk_csv/dict_writer.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def _encode_list(self, value):
6363
multi_value = ';'.join(
6464
['$' + DictWriter._to_string(item).replace('$', '$$') + '$' for item
6565
in value])
66-
value = self._mv_delimiter.join([repr(item) for item in value])
66+
value = self._mv_delimiter.join([DictWriter._to_string(item) for item in value])
6767
return value, multi_value
6868

6969
def _header_written(self):
@@ -73,8 +73,8 @@ def _header_written(self):
7373
def _to_string(item):
7474
if isinstance(item, bool):
7575
return 't' if item else 'f'
76-
if isinstance(item, str):
77-
return item
76+
if isinstance(item, basestring):
77+
return item.decode('utf-8')
7878
if isinstance(item, Number):
7979
return str(item)
8080
return repr(item)

splunklib/searchcommands/validators.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import re
2323
import sys
2424

25-
2625
class Validator(object):
2726
""" Base class for validators that check and format search command options.
2827
@@ -123,12 +122,12 @@ def check_range(value):
123122
elif minimum is not None:
124123
def check_range(value):
125124
if value < minimum:
126-
raise ValueError('Expected integer in the range [-∞,%d]: %d' % (maximum, value))
125+
raise ValueError('Expected integer in the range [%d,+∞]: %d' % (minimum, value))
127126
return
128127
elif maximum is not None:
129128
def check_range(value):
130129
if value > maximum:
131-
raise ValueError('Expected integer in the range [%d,+∞]: %d' % (minimum, value))
130+
raise ValueError('Expected integer in the range [-∞,%d]: %d' % (maximum, value))
132131
return
133132
else:
134133
def check_range(value):
@@ -144,7 +143,7 @@ def __call__(self, value):
144143
return value
145144

146145
def format(self, value):
147-
return str(value)
146+
return str(int(value))
148147

149148

150149
class Duration(Validator):
@@ -214,6 +213,24 @@ def format(self, value):
214213
return value[:-1]
215214

216215

216+
class Map(Validator):
217+
""" Validates map option values.
218+
219+
"""
220+
def __init__(self, **kwargs):
221+
self.membership = kwargs
222+
223+
def __call__(self, value):
224+
if value is not None:
225+
value = str(value)
226+
if value not in self.membership:
227+
raise ValueError('Unrecognized value: %s' % value)
228+
return self.membership[value]
229+
230+
def format(self, value):
231+
return self.membership.keys()[self.membership.values().index(value)]
232+
233+
217234
class OptionName(Validator):
218235
""" Validates option names.
219236
@@ -226,6 +243,9 @@ def __call__(self, value):
226243
raise ValueError('Illegal characters in option name: %s' % value)
227244
return value
228245

246+
def format(self, value):
247+
return self.__call__(value)
248+
229249

230250
class RegularExpression(Validator):
231251
""" Validates regular expression option values.
@@ -256,3 +276,6 @@ def __call__(self, value):
256276
if value not in self.membership:
257277
raise ValueError('Unrecognized value: %s' % value)
258278
return value
279+
280+
def format(self, value):
281+
return self.__call__(value)

0 commit comments

Comments
 (0)